From a4476ebeb58942facd63918cc3be232fcbaa478e Mon Sep 17 00:00:00 2001 From: Paula Date: Wed, 20 May 2026 17:06:35 +0200 Subject: [PATCH] allow multiple time dependent release files for multiple parallel simulations runs update docu fix bug, allow specifying timedependent release scenario in cfg file, work with scenario names instead of file paths check if chosen release scenario is available is earlier now --- avaframe/com1DFA/com1DFA.py | 31 +++++-- avaframe/com1DFA/com1DFACfg.ini | 9 +- avaframe/com1DFA/com1DFATools.py | 1 - avaframe/com1DFA/debrisFunctions.py | 63 +------------ avaframe/com1DFA/deriveParameterSet.py | 11 ++- avaframe/in1Data/getInput.py | 120 +++++++++++++++++++++---- avaframe/tests/test_com1DFA.py | 18 +++- avaframe/tests/test_debrisFunctions.py | 59 ------------ avaframe/tests/test_getInput.py | 92 ++++++++++++++++++- docs/moduleCom1DFA.rst | 2 +- 10 files changed, 252 insertions(+), 154 deletions(-) diff --git a/avaframe/com1DFA/com1DFA.py b/avaframe/com1DFA/com1DFA.py index d4cf92d82..50d34963f 100644 --- a/avaframe/com1DFA/com1DFA.py +++ b/avaframe/com1DFA/com1DFA.py @@ -467,10 +467,8 @@ def prepareReleaseEntrainment(cfg, rel, inputSimLines): badName = False if "_" in relName: badName = True - log.warning( - "Release area scenario file name includes an underscore \ - the suffix _AF will be added for the simulation name" - ) + log.warning("Release area scenario file name includes an underscore \ + the suffix _AF will be added for the simulation name") # set release thickness if cfg["GENERAL"].getboolean("timeDependentRelease"): @@ -639,7 +637,7 @@ def prepareInputData(inputSimFiles, cfg): # get line from release area polygon if cfg["GENERAL"].getboolean("timeDependentRelease"): releaseLine["type"] = "time dependent Release" - timeDepRelValues, _ = gI.getTimeDepRelCsv(inputSimFiles["timeDepRelCsv"]) + timeDepRelValues, _ = gI.getTimeDepRelCsv(cfg["INPUT"]["timeDepRelCsv"]) releaseLine["thickness"] = [ timeDepRelValues["thickness"][timeDepRelValues["timeStep"] == 0].item() ] * len(releaseLine["Name"]) @@ -763,7 +761,7 @@ def prepareInputData(inputSimFiles, cfg): damLine = None if cfg["GENERAL"].getboolean("timeDependentRelease"): - releaseLine = debF.prepareTimeDepRelLine(inputSimFiles, releaseLine, cfg) + releaseLine = debF.prepareTimeDepRelLine(releaseLine, cfg) inputSimLines = { "releaseLine": releaseLine, @@ -3351,11 +3349,26 @@ def prepareVarSimDict(standardCfg, inputSimFiles, variationDict, simNameExisting inputSimFiles["entResInfo"]["secondaryRelRemeshed"] = remeshedSecRel if cfgSim["GENERAL"]["timeDependentRelease"] == "True": - cfgSim["INPUT"]["timeDepRelCsv"] = inputSimFiles["timeDepRelCsv"] - timeDepRelValues, _ = gI.getTimeDepRelCsv(inputSimFiles["timeDepRelCsv"]) + cfgSim["INPUT"]["timeDepRelCsv"] = pathlib.Path( + cfgSim["GENERAL"]["avalancheDir"], + "Inputs", + "REL", + (cfgSim["GENERAL"]["timeDependentReleaseScenarios"] + ".csv"), + ) + if cfgSim["INPUT"]["timeDepRelCsv"].exists() is False: + message = ( + "time dependent release file: %s file in Inputs/REL with file ending .csv not found" + % (cfgSim["GENERAL"]["timeDependentReleaseScenarios"]) + ) + log.error(message) + raise FileNotFoundError(message) + + timeDepRelValues, _ = gI.getTimeDepRelCsv(cfgSim["INPUT"]["timeDepRelCsv"]) cfgSim["INPUT"]["timeDepRelTimeStep"] = str(timeDepRelValues["timeStep"]) cfgSim["INPUT"]["timeDepRelThickness"] = str(timeDepRelValues["thickness"]) cfgSim["INPUT"]["timeDepRelVelocity"] = str(timeDepRelValues["velocity"]) + else: + cfgSim["INPUT"]["timeDepRelCsv"] = "" if modName in ["com1DFA", "com5SnowSlide", "com6RockAvalanche"]: # check if spatialVoellmy is chosen that friction fields have correct extent @@ -3460,7 +3473,7 @@ def prepareVarSimDict(standardCfg, inputSimFiles, variationDict, simNameExisting cfgSim, pathToDemFull, inputSimFiles["secondaryRelFile"], - timeDepRelFile=inputSimFiles["timeDepRelCsv"], + timeDepRelFile=cfgSim["INPUT"]["timeDepRelCsv"], ) else: relVolume = "" diff --git a/avaframe/com1DFA/com1DFACfg.ini b/avaframe/com1DFA/com1DFACfg.ini index 0927cca80..085c50f37 100644 --- a/avaframe/com1DFA/com1DFACfg.ini +++ b/avaframe/com1DFA/com1DFACfg.ini @@ -132,10 +132,15 @@ entThDistVariation = # entrainment thickness (only considered if ENT file is shapefile and entThFromFile=False) [m] entTh = -#+++++++++++++general start conditions: time dependent release -# if timeDependentRelease is True, provide the the timesteps, thickness and velocity +#+++++++++++++General start conditions: time dependent release +# if timeDependentRelease is True (and relThFromFile is True), provide the the timesteps, thickness and velocity # for a releases in a csv-file in the REL folder timeDependentRelease = False +# specify one or multiple particular time dependent release files, +# provide name of csv file with or without extension .csv +# multiple files are separated with a | +# if no file is specified, all csv files in the Inputs/REL folder are computed +timeDependentReleaseScenarios = # when checking if an already existing particle is within a release polygon # (that would cause numerical instabilities and rise an error), one can decide to add a buffer # around the polygon (0 means take strictly the points inside, a very small value diff --git a/avaframe/com1DFA/com1DFATools.py b/avaframe/com1DFA/com1DFATools.py index 5ba11a87a..5abd0d857 100644 --- a/avaframe/com1DFA/com1DFATools.py +++ b/avaframe/com1DFA/com1DFATools.py @@ -20,7 +20,6 @@ from avaframe.in3Utils import cfgUtils from avaframe.in2Trans import rasterUtils as IOf - # create local logger # change log level in calling module to DEBUG to see log messages log = logging.getLogger(__name__) diff --git a/avaframe/com1DFA/debrisFunctions.py b/avaframe/com1DFA/debrisFunctions.py index 9a63c13d1..08410304d 100644 --- a/avaframe/com1DFA/debrisFunctions.py +++ b/avaframe/com1DFA/debrisFunctions.py @@ -145,59 +145,7 @@ def addReleaseParticles(cfg, particles, inputSimLines, thickness, velocityMag, d return particles, zPartArray0 -def checkTimeDepRelease(timeDepRelValues, timeDepRelCsv): - """ - check if time dependent release values satisfy the following requirements: - - release - timesteps are unique - - the release - timesteps are not too close (that the particle density becomes too high) - - provided release - thickness is larger than zero - - provided velocity is zero or larger. - - Parameters - ----------- - timeDepRelCsv: str - directory to csv table containing time dependent release values - timeDepRelValues: dict - contains time dependent release values: timestep, thickness, velocity - """ - # check if timesteps are unique - timeStepUnique = np.unique(timeDepRelValues["timeStep"]) - if timeStepUnique.ndim == 0: - if timeStepUnique != timeDepRelValues["timeStep"]: - message = "The provided time dependent release time steps in %s are not unique" % (timeDepRelCsv) - log.error(message) - raise ValueError(message) - elif len(timeStepUnique) != len(timeDepRelValues["timeStep"]): - message = "The provided time dependent release timesteps in %s are not unique" % (timeDepRelCsv) - log.error(message) - raise ValueError(message) - - # check if a timestep = 0 is provided - if 0 not in timeStepUnique: - message = ( - "If release is time dependent, a thickness needs to be provided for time step 0 s in %s" - % (timeDepRelCsv) - ) - log.error(message) - raise ValueError(message) - - # check that release thickness > 0 - for th in timeDepRelValues["thickness"]: - if th <= 0: - message = "For every release time step a thickness > 0 needs to be provided in %s" % ( - timeDepRelCsv - ) - log.error(message) - raise ValueError(message) - - for vel in timeDepRelValues["velocity"]: - if vel < 0: - message = "The initial velocity provided in %s can not be negative." % (timeDepRelCsv) - log.error(message) - raise ValueError(message) - - -def prepareTimeDepRelLine(inputSimFiles, releaseLine, cfg): +def prepareTimeDepRelLine(releaseLine, cfg): """ read time dependent release values and return them as a dictionary containing: - timestep @@ -207,9 +155,6 @@ def prepareTimeDepRelLine(inputSimFiles, releaseLine, cfg): Parameters ---------- - inputSimFiles : dict - dictionary containing - - timeDepRelCsv: str, path to time dependent release values (csv-)file releaseLine: dict contains information of release line cfg: configparser object @@ -224,14 +169,12 @@ def prepareTimeDepRelLine(inputSimFiles, releaseLine, cfg): """ try: - releaseLine["values"], timeDepRelValuesDF = gI.getTimeDepRelCsv(inputSimFiles["timeDepRelCsv"]) + releaseLine["values"], timeDepRelValuesDF = gI.getTimeDepRelCsv(cfg["INPUT"]["timeDepRelCsv"]) releaseLine["thicknessSource"] = ["csv file"] except: - message = "No time dependent release csv file found" + message = "Provide a valid csv file containing time dependent release values" log.error(message) raise FileNotFoundError(message) - # check if some criterias are satisfied in the csv file - checkTimeDepRelease(releaseLine["values"], inputSimFiles["timeDepRelCsv"]) # write the time dependent values into configurationFiles folder cfgUtils.writeReleaseCsvFile(cfg, timeDepRelValuesDF) diff --git a/avaframe/com1DFA/deriveParameterSet.py b/avaframe/com1DFA/deriveParameterSet.py index c80bcc8ca..6b536284d 100644 --- a/avaframe/com1DFA/deriveParameterSet.py +++ b/avaframe/com1DFA/deriveParameterSet.py @@ -433,8 +433,15 @@ def checkThicknessSettings(cfg, thName, inputSimFiles): log.error(message) raise FileNotFoundError(message) if ( - cfg["GENERAL"].getboolean("timeDependentRelease") - and cfg["GENERAL"].getboolean("relThFromFile") is False + cfg["GENERAL"].getboolean("timeDependentRelease") + and inputSimFiles["entResInfo"]["timeDepRelCsvAvailable"] == "No" + ): + message = "When release is time dependent, a csv file containing time dependent release parameters needs to be provided." + log.error(message) + raise FileNotFoundError(message) + if ( + cfg["GENERAL"].getboolean("timeDependentRelease") + and cfg["GENERAL"].getboolean("relThFromFile") is False ): message = "When release is time dependent, relThFromFile needs to be set to True" log.error(message) diff --git a/avaframe/in1Data/getInput.py b/avaframe/in1Data/getInput.py index acf4e7ac2..4f9c451db 100644 --- a/avaframe/in1Data/getInput.py +++ b/avaframe/in1Data/getInput.py @@ -226,6 +226,10 @@ def getInputDataCom1DFA(avaDir): message = "Release area information - use either .shp or .asc/.tif files" log.error(message) raise AssertionError(message) + if len(relFiles) == 0: + message = "No release area is found - provide a .shp or .asc or .tif file" + log.error(message) + raise FileNotFoundError(message) else: log.info("Release area files are: %s" % [str(relFilestr) for relFilestr in relFiles]) entResInfo["relThFileType"] = relFiles[0].suffix @@ -297,9 +301,11 @@ def getInputDataCom1DFA(avaDir): entResInfo["resRemeshed"] = "No" entResInfo["bhdRemeshed"] = "No" - timeDepRelCsv, entResInfo["timeDepRelCsv"], _ = getAndCheckInputFiles( - inputDir, "REL", "Time dependent release parameters (csv)", fileExt="csv" - ) + timeDepRelFiles = sorted(list(releaseDir.glob("*.csv"))) + if len(timeDepRelFiles) > 0: + entResInfo["timeDepRelCsvAvailable"] = "Yes" + else: + entResInfo["timeDepRelCsvAvailable"] = "No" # return DEM, first item of release, entrainment and resistance areas inputSimFiles = { @@ -315,7 +321,7 @@ def getInputDataCom1DFA(avaDir): "kFile": kFile, "tauCFile": tauCFile, "bhdFile": bhdFile, - "timeDepRelCsv": timeDepRelCsv, + "timeDepRelCsv": timeDepRelFiles, } for thFile in ["rel", "secondaryRel", "ent"]: @@ -362,7 +368,7 @@ def getAndCheckInputFiles(inputDir, folder, inputType, fileExt="shp", fileSuffix """ available = "No" - supportedFileFormats = [".shp", ".asc", ".tif", ".csv"] + supportedFileFormats = [".shp", ".asc", ".tif"] # Define the directory to search and the extensions if fileExt == "": @@ -401,8 +407,7 @@ def getAndCheckInputFiles(inputDir, folder, inputType, fileExt="shp", fileSuffix if OutputFile.suffix not in supportedFileFormats: message = ( - "Unsupported file format found for OutputFile %s; shp, asc, tif, csv are allowed" - % OutputFile + "Unsupported file format found for OutputFile %s; shp, asc, tif are allowed" % OutputFile ) log.error(message) raise AssertionError(message) @@ -498,12 +503,19 @@ def updateThicknessCfg(inputSimFiles, cfgInitial): thTypeList.append("entFile") if cfgInitial["GENERAL"].getboolean("secRelArea"): thTypeList.append("secondaryRelFile") + if cfgInitial["GENERAL"].getboolean("timeDependentRelease"): + thTypeList.append("timeDepRelFile") # initialize release scenario list releaseScenarioIni = cfgInitial["INPUT"]["releaseScenario"] if releaseScenarioIni == "": releaseScenarioList = inputSimFiles["releaseScenarioList"] else: + for scenario in cfgInitial["INPUT"]["releaseScenario"].split("|"): + if scenario not in inputSimFiles["releaseScenarioList"]: + message = "Chosen release scenario: %s not available" % scenario + log.error(message) + raise FileNotFoundError(message) releaseScenarioList = cfgInitial["INPUT"]["releaseScenario"].split("|") # add input data info to cfg object @@ -539,18 +551,40 @@ def updateThicknessCfg(inputSimFiles, cfgInitial): ) cfgInitial["INPUT"]["secondaryReleaseScenario"] = inputSimFiles["secondaryRelFile"].stem + # get time dependent release scenario + if inputSimFiles["timeDepRelCsv"] is not None and "timeDepRelFile" in thTypeList: + timeDepRelFileIni = cfgInitial["GENERAL"]["timeDependentReleaseScenarios"] + availableTimeDepRelScenarios = [] + for file in inputSimFiles["timeDepRelCsv"]: + availableTimeDepRelScenarios.append(file.stem) + + if timeDepRelFileIni == "": + # if no scenario is specified in the ini file, use all available csv files + timeDepRelScenarioList = availableTimeDepRelScenarios + else: + # use specified scenario + timeDepRelScenarioList = [] + for timeDepScenario in cfgInitial["GENERAL"]["timeDependentReleaseScenarios"].split("|"): + timeDepRelScenarioList.append(pathlib.Path(timeDepScenario).stem) + + timeDepRelScenariosCfg = cfgUtils.convertToCfgList(timeDepRelScenarioList) + if timeDepRelFileIni == "": + cfgInitial["GENERAL"]["timeDependentReleaseScenarios"] = timeDepRelScenariosCfg + # check if a csv file exists for the specified scenario(s) + for timeDepIniFileName in cfgInitial["GENERAL"]["timeDependentReleaseScenarios"].split("|"): + timeDepIniFileName = pathlib.Path(timeDepIniFileName).stem + if timeDepIniFileName not in availableTimeDepRelScenarios: + message = "Chosen time dependent release scenario: %s not available" % timeDepIniFileName + log.error(message) + raise FileNotFoundError(message) + else: + cfgInitial["GENERAL"]["timeDependentReleaseScenarios"] = timeDepRelScenariosCfg + # create cfg string from release scenario list and add to cfg object releaseScenarioName = cfgUtils.convertToCfgList(releaseScenarioList) if cfgInitial["INPUT"]["releaseScenario"] == "": cfgInitial["INPUT"]["releaseScenario"] = releaseScenarioName - else: - for relIniFileName in cfgInitial["INPUT"]["releaseScenario"].split("|"): - if relIniFileName not in releaseScenarioList: - message = "Chosen release scenario: %s not available" % relIniFileName - log.error(message) - raise FileNotFoundError(message) - else: - log.info("Chosen release scenarios: %s" % cfgInitial["INPUT"]["releaseScenario"]) + log.info("Chosen release scenarios: %s" % cfgInitial["INPUT"]["releaseScenario"]) return cfgInitial @@ -705,7 +739,7 @@ def fetchReleaseFile(inputSimFiles, releaseScenario, cfgSim, releaseList): releaseScenarioPath.parts[-2] + "/" + releaseScenarioPath.parts[-1] ) elif ( - cfgSim["GENERAL"]["relThFromFile"] == "True" and cfgSim["GENERAL"]["timeDependentRelease"] == "False" + cfgSim["GENERAL"]["relThFromFile"] == "True" and cfgSim["GENERAL"]["timeDependentRelease"] == "False" ): # shapefile with thickness attributes - handle thickness/id/ci95 values for scenario in releaseList: @@ -1177,4 +1211,58 @@ def getTimeDepRelCsv(timeDepRelCsv): "thickness": timeDepRelDF["thickness"].to_numpy(dtype=np.float64), "velocity": timeDepRelDF["velocity"].to_numpy(dtype=np.float64), } + # check if some criterias are satisfied in the csv file + checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) return timeDepRelValues, timeDepRelDF + + +def checkTimeDepRelease(timeDepRelValues, timeDepRelCsv): + """ + check if time dependent release values satisfy the following requirements: + - release - timesteps are unique + - the release - timesteps are not too close (that the particle density becomes too high) + - provided release - thickness is larger than zero + - provided velocity is zero or larger. + + Parameters + ----------- + timeDepRelCsv: str + directory to csv table containing time dependent release values + timeDepRelValues: dict + contains time dependent release values: timestep, thickness, velocity + """ + # check if timesteps are unique + timeStepUnique = np.unique(timeDepRelValues["timeStep"]) + if timeStepUnique.ndim == 0: + if timeStepUnique != timeDepRelValues["timeStep"]: + message = "The provided time dependent release time steps in %s are not unique" % (timeDepRelCsv) + log.error(message) + raise ValueError(message) + elif len(timeStepUnique) != len(timeDepRelValues["timeStep"]): + message = "The provided time dependent release timesteps in %s are not unique" % (timeDepRelCsv) + log.error(message) + raise ValueError(message) + + # check if a timestep = 0 is provided + if 0 not in timeStepUnique: + message = ( + "If release is time dependent, a thickness needs to be provided for time step 0 s in %s" + % (timeDepRelCsv) + ) + log.error(message) + raise ValueError(message) + + # check that release thickness > 0 + for th in timeDepRelValues["thickness"]: + if th <= 0: + message = "For every release time step a thickness > 0 needs to be provided in %s" % ( + timeDepRelCsv + ) + log.error(message) + raise ValueError(message) + + for vel in timeDepRelValues["velocity"]: + if vel < 0: + message = "The initial velocity provided in %s can not be negative." % (timeDepRelCsv) + log.error(message) + raise ValueError(message) diff --git a/avaframe/tests/test_com1DFA.py b/avaframe/tests/test_com1DFA.py index 64cca3b46..70592941f 100644 --- a/avaframe/tests/test_com1DFA.py +++ b/avaframe/tests/test_com1DFA.py @@ -54,6 +54,7 @@ def test_prepareInputData(tmp_path): cfg["INPUT"] = {"DEM": "avaAlr.tif"} cfg["INPUT"]["relThFile"] = "" cfg["INPUT"]["entThFile"] = "" + cfg["INPUT"]["timeDepRelCsv"] = "" # call function to be tested demOri, inputSimLines = com1DFA.prepareInputData(inputSimFiles, cfg) @@ -231,6 +232,7 @@ def test_prepareInputData(tmp_path): } cfg["INPUT"] = {"DEM": "testDEM.asc"} cfg["INPUT"]["relThFile"] = str(inputSimFiles["relThFile"]) + cfg["INPUT"]["timeDepRelCsv"] = "" demOri, inputSimLines = com1DFA.prepareInputData(inputSimFiles, cfg) @@ -278,6 +280,7 @@ def test_prepareInputData(tmp_path): cfg["INPUT"] = {"DEM": "testDEM.asc"} cfg["INPUT"]["relThFile"] = "" cfg["INPUT"]["secondaryRelThFile"] = str(inputSimFiles["secondaryRelThFile"]) + cfg["INPUT"]["timeDepRelCsv"] = "" demOri, inputSimLines = com1DFA.prepareInputData(inputSimFiles, cfg) @@ -322,6 +325,7 @@ def test_prepareInputData(tmp_path): } cfg["INPUT"] = {"DEM": "testDEM.asc"} cfg["INPUT"]["relThFile"] = str(inputSimFiles["relThFile"]) + cfg["INPUT"]["timeDepRelCsv"] = "" # with pytest.raises(AssertionError) as e: # assert com1DFA.prepareInputData(inputSimFiles, cfg) @@ -348,6 +352,7 @@ def test_prepareInputData(tmp_path): } cfg["INPUT"] = {"DEM": "avaAlr.tif"} cfg["INPUT"]["relThFile"] = "" + cfg["INPUT"]["timeDepRelCsv"] = "" with pytest.raises(AssertionError) as e: assert com1DFA.prepareInputData(inputSimFiles, cfg) @@ -380,6 +385,7 @@ def test_prepareInputData(tmp_path): cfg["INPUT"]["relThFile"] = "" cfg["INPUT"]["entThFile"] = "" cfg["INPUT"]["releaseScenario"] = "release1PF" + cfg["INPUT"]["timeDepRelCsv"] = str(avaDir / "Inputs" / "REL" / "release1PF.csv") # call function to be tested demOri, inputSimLines = com1DFA.prepareInputData(inputSimFiles, cfg) @@ -1837,7 +1843,7 @@ def test_savePartToPickle(tmp_path): particlesRead7 = pickle.load(open(picklePath7, "rb")) for pProp in particlesRead7: - assert pProp in ['ux', 'uy', 'uz', 'iCell', 'z', 'x', 'y', 'm', 'h', 't'] + assert pProp in ["ux", "uy", "uz", "iCell", "z", "x", "y", "m", "h", "t"] def test_exportFields(tmp_path): @@ -2096,6 +2102,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "True", "explicitFriction": 0, "timeDependentRelease": "False", + "timeDependentReleaseScenarios": "", } standardCfg["INPUT"] = { "entThThickness": "1.", @@ -2103,6 +2110,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "entThCi95": "None", "releaseScenario": "", "relThFile": "", + "timeDepRelCsv": "", } testDir = pathlib.Path(__file__).parents[0] @@ -2170,6 +2178,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "True", "explicitFriction": 0, "timeDependentRelease": "False", + "timeDependentReleaseScenarios": "", } testCfg["INPUT"] = { @@ -2177,6 +2186,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "entThId": "0", "entThCi95": "None", "releaseScenario": "relAlr", + "timeDepRelCsv": "", } testCfg["INPUT"]["DEM"] = "avaAlr.tif" testCfg["INPUT"]["relThFile"] = "" @@ -2281,6 +2291,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "True", "explicitFriction": 0, "timeDependentRelease": "False", + "timeDependentReleaseScenarios": "", } testCfg2["INPUT"] = { "entThThickness": "1.", @@ -2288,6 +2299,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "entThCi95": "None", "releaseScenario": "relAlr", "DAM": str(pathlib.Path("DAM", relPath.name)), + "timeDepRelCsv": "", } testCfg2["INPUT"]["DEM"] = "avaAlr.tif" testCfg2["INPUT"]["relThFile"] = "" @@ -2366,6 +2378,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "entThCi95": "None", "releaseScenario": "", "relThFile": "", + "timeDepRelCsv": "", } testDir = pathlib.Path(__file__).parents[0] @@ -2377,6 +2390,8 @@ def test_prepareVarSimDict(tmp_path, caplog): standardCfg["INPUT"]["DEM"] = "DEM_PF_Topo.asc" standardCfg["GENERAL"]["avalancheDir"] = str(avaDir) + standardCfg["GENERAL"]["timeDependentReleaseScenarios"] = "release1PF" + relPath = pathlib.Path(avaDir, "Inputs", "REL", "release1PF.shp") inputSimFiles = { "relFiles": [relPath], @@ -2432,6 +2447,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "False", "explicitFriction": 0, "timeDependentRelease": "True", + "timeDependentReleaseScenarios": "release1PF", } testCfg["INPUT"] = { diff --git a/avaframe/tests/test_debrisFunctions.py b/avaframe/tests/test_debrisFunctions.py index 8781ff7e4..92c01f8a0 100644 --- a/avaframe/tests/test_debrisFunctions.py +++ b/avaframe/tests/test_debrisFunctions.py @@ -150,65 +150,6 @@ def test_addReleaseParticles(): debF.addReleaseParticles(cfg, particles, inputSimLines, thickness, velocityMag, dem, zPartArray0) -def test_checkTimeDepRelease(): - timeDepRelValues = { - "timeStep": np.array([0, 10, 20, 30, 35]), - "thickness": np.array([1, 1, 1, 1, 1]), - "velocity": np.array([10, 10, 10, 10, 10]), - } - timeDepRelCsv = "path2TimeDepRel.csv" - # no error should occur - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - - timeDepRelValues["timeStep"] = np.array([20, 10, 0, 30, 35]) - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - - # timesteps are not unique - timeDepRelValues["timeStep"] = np.array([0, 10, 10, 30, 35]) - - with pytest.raises(ValueError) as e: - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - assert ("The provided time dependent release timesteps in %s are not unique" % (timeDepRelCsv)) in str( - e.value - ) - - timeDepRelValues["timeStep"] = np.array([0, 0]) - with pytest.raises(ValueError) as e: - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - assert ("The provided time dependent release timesteps in %s are not unique" % (timeDepRelCsv)) in str( - e.value - ) - - # no timestep 0 - timeDepRelValues["timeStep"] = np.array([20, 15, 10, 30, 35]) - - with pytest.raises(ValueError) as e: - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - assert ( - "If release is time dependent, a thickness needs to be provided for time step 0 s in %s" - % (timeDepRelCsv) - ) in str(e.value) - - # thickness needs to be > 0 - timeDepRelValues["timeStep"] = np.array([20, 10, 0, 30, 35]) - timeDepRelValues["thickness"] = np.array([1, 0, 1, 1, 1]) - with pytest.raises(ValueError) as e: - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - assert ("For every release time step a thickness > 0") in str(e.value) - - timeDepRelValues["thickness"] = np.array([1, 1, -1, 1, 1]) - with pytest.raises(ValueError) as e: - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - assert ("For every release time step a thickness > 0") in str(e.value) - - # velocity needs to be >= 0 - timeDepRelValues["thickness"] = np.array([1, 1, 1, 1, 1]) - timeDepRelValues["velocity"] = np.array([10, 10, 10, -10, 10]) - with pytest.raises(ValueError) as e: - debF.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) - assert ("The initial velocity provided in %s can not be negative." % (timeDepRelCsv)) in str(e.value) - - """ Test does not word because: When calling pytest, executing DFAfunctionsCython.upfateFieldsC() raises an error ("Fatal Python error: Aborted") (see issue #1002?) diff --git a/avaframe/tests/test_getInput.py b/avaframe/tests/test_getInput.py index 3944101a1..553556fec 100644 --- a/avaframe/tests/test_getInput.py +++ b/avaframe/tests/test_getInput.py @@ -309,6 +309,7 @@ def test_updateThicknessCfg(tmp_path): cfg["GENERAL"]["relThFromFile"] = "True" cfg["GENERAL"]["simTypeList"] = "null|ent" cfg["GENERAL"]["secRelAra"] = "False" + cfg["GENERAL"]["timeDependentRelease"] = "False" cfg["INPUT"] = {"releaseScenario": ""} demFile = avaTestDirInputs / "DEM_HS_Topo.asc" @@ -332,6 +333,7 @@ def test_updateThicknessCfg(tmp_path): "releaseScenarioList": ["release1HS", "release2HS"], "seondaryRelThFile": None, "entThFile": None, + "timeDepRelCsv": None, } inputSimFiles["release1HS"] = {"thickness": ["1.0"], "id": ["0"], "ci95": ["None", "None"]} @@ -358,6 +360,30 @@ def test_updateThicknessCfg(tmp_path): assert cfg["INPUT"]["entThId"] == "0" assert cfg["INPUT"]["entThThickness"] == "0.3" + # test with time dependent release option + + cfg["GENERAL"]["timeDependentRelease"] = "True" + cfg["GENERAL"]["timeDependentReleaseScenarios"] = "" + inputSimFiles["timeDepRelCsv"] = [ + avaTestDirInputs / "REL" / "relTest1.csv", + avaTestDirInputs / "REL" / "relTest2.csv", + ] + + cfg = getInput.updateThicknessCfg(inputSimFiles, cfg) + + assert cfg["GENERAL"]["timeDependentReleaseScenarios"] == "relTest1|relTest2" + + cfg["GENERAL"]["timeDependentRelease"] = "True" + cfg["GENERAL"]["timeDependentReleaseScenarios"] = "relTest1.csv" + inputSimFiles["timeDepRelCsv"] = [ + avaTestDirInputs / "REL" / "relTest1.csv", + avaTestDirInputs / "REL" / "relTest2.csv", + ] + + cfg = getInput.updateThicknessCfg(inputSimFiles, cfg) + + assert cfg["GENERAL"]["timeDependentReleaseScenarios"] == "relTest1" + def test_selectReleaseFile(tmp_path): """testing selecting a release area scenario according to configuration settings""" @@ -1344,6 +1370,7 @@ def test_updateThicknessCfg_with_specified_scenarios(tmp_path): }, "relThFile": None, "releaseScenarioList": ["release1HS", "release2HS"], + "timeDepRelCsv": None, } inputSimFiles["release1HS"] = {"thickness": ["1.0"], "id": ["0"], "ci95": ["None"]} @@ -1400,6 +1427,7 @@ def test_updateThicknessCfg_with_secondary_release_raster(tmp_path): }, "relThFile": None, "releaseScenarioList": ["release1HS"], + "timeDepRelCsv": None, } inputSimFiles["release1HS"] = {"thickness": ["1.0"], "id": ["0"], "ci95": ["None"]} @@ -1556,11 +1584,69 @@ def test_getTimeDepRelCsv(): testDir = pathlib.Path(__file__).parents[0] timeDepRelCsv = testDir / "data" / "testTimeDepRel" / "rel.csv" - timeDepRelValues, timeDepRelValuesTxt = getInput.getTimeDepRelCsv(timeDepRelCsv) - assert np.all(timeDepRelValues["timeStep"] == np.array([0, 20, 50])) - assert np.all(timeDepRelValues["thickness"] == np.array([3, 1, 0])) + with pytest.raises(ValueError): + timeDepRelValues, timeDepRelValuesTxt = getInput.getTimeDepRelCsv(timeDepRelCsv) timeDepRelCsv = testDir / "data" / "testTimeDepRel" / "rel_notSorted.csv" timeDepRelValues, timeDepRelValuesTxt = getInput.getTimeDepRelCsv(timeDepRelCsv) assert np.all(timeDepRelValues["timeStep"] == np.array([0, 20, 50])) assert np.all(timeDepRelValues["thickness"] == np.array([3, 1, 1])) + + +def test_checkTimeDepRelease(): + timeDepRelValues = { + "timeStep": np.array([0, 10, 20, 30, 35]), + "thickness": np.array([1, 1, 1, 1, 1]), + "velocity": np.array([10, 10, 10, 10, 10]), + } + timeDepRelCsv = "path2TimeDepRel.csv" + # no error should occur + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + + timeDepRelValues["timeStep"] = np.array([20, 10, 0, 30, 35]) + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + + # timesteps are not unique + timeDepRelValues["timeStep"] = np.array([0, 10, 10, 30, 35]) + + with pytest.raises(ValueError) as e: + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + assert ("The provided time dependent release timesteps in %s are not unique" % (timeDepRelCsv)) in str( + e.value + ) + + timeDepRelValues["timeStep"] = np.array([0, 0]) + with pytest.raises(ValueError) as e: + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + assert ("The provided time dependent release timesteps in %s are not unique" % (timeDepRelCsv)) in str( + e.value + ) + + # no timestep 0 + timeDepRelValues["timeStep"] = np.array([20, 15, 10, 30, 35]) + + with pytest.raises(ValueError) as e: + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + assert ( + "If release is time dependent, a thickness needs to be provided for time step 0 s in %s" + % (timeDepRelCsv) + ) in str(e.value) + + # thickness needs to be > 0 + timeDepRelValues["timeStep"] = np.array([20, 10, 0, 30, 35]) + timeDepRelValues["thickness"] = np.array([1, 0, 1, 1, 1]) + with pytest.raises(ValueError) as e: + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + assert ("For every release time step a thickness > 0") in str(e.value) + + timeDepRelValues["thickness"] = np.array([1, 1, -1, 1, 1]) + with pytest.raises(ValueError) as e: + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + assert ("For every release time step a thickness > 0") in str(e.value) + + # velocity needs to be >= 0 + timeDepRelValues["thickness"] = np.array([1, 1, 1, 1, 1]) + timeDepRelValues["velocity"] = np.array([10, 10, 10, -10, 10]) + with pytest.raises(ValueError) as e: + getInput.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) + assert ("The initial velocity provided in %s can not be negative." % (timeDepRelCsv)) in str(e.value) diff --git a/docs/moduleCom1DFA.rst b/docs/moduleCom1DFA.rst index 81b12941c..31ad44898 100644 --- a/docs/moduleCom1DFA.rst +++ b/docs/moduleCom1DFA.rst @@ -177,7 +177,7 @@ input file (shape file or raster file) or 2) through the :py:mod:`com1DFA` confi - if the flag `timeDependentRelease` is True, in various provided time steps flowing mass is initialized (`relThFromFile` is also set to True, currently the only option to read time dependent thickness is from csv file) - - additional to a .shp file (raster file does not work yet), a csv file is provided in the `REL` folder, that contains: + - additional to a .shp file (raster file does not work yet), at least one csv file is provided in the `REL` folder, that contains: - a header (first line) - the following columns with the respective column names: