diff --git a/avaframe/com1DFA/com1DFA.py b/avaframe/com1DFA/com1DFA.py index a81887bef..d6057acf7 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"]) @@ -3401,11 +3399,18 @@ 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"), + ) + 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 @@ -3510,7 +3515,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..466921ab3 100644 --- a/avaframe/com1DFA/debrisFunctions.py +++ b/avaframe/com1DFA/debrisFunctions.py @@ -224,14 +224,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..f6517ea5f 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"]["timeDepRelCsv"] == "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..84da6baf8 100644 --- a/avaframe/in1Data/getInput.py +++ b/avaframe/in1Data/getInput.py @@ -18,6 +18,7 @@ import avaframe.in2Trans.shpConversion as shpConv import avaframe.in3Utils.fileHandlerUtils as fU import avaframe.in3Utils.geoTrans as geoTrans +from avaframe.com1DFA import debrisFunctions # Local imports from avaframe.in3Utils import cfgUtils @@ -226,6 +227,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 are 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 +302,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["timeDepRelCsv"] = "Yes" + else: + entResInfo["timeDepRelCsv"] = "No" # return DEM, first item of release, entrainment and resistance areas inputSimFiles = { @@ -315,7 +322,7 @@ def getInputDataCom1DFA(avaDir): "kFile": kFile, "tauCFile": tauCFile, "bhdFile": bhdFile, - "timeDepRelCsv": timeDepRelCsv, + "timeDepRelCsv": timeDepRelFiles, } for thFile in ["rel", "secondaryRel", "ent"]: @@ -362,7 +369,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 +408,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,6 +504,8 @@ 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"] @@ -539,6 +547,36 @@ 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 + else: + # 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"] == "": @@ -705,7 +743,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 +1215,6 @@ 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 + debrisFunctions.checkTimeDepRelease(timeDepRelValues, timeDepRelCsv) return timeDepRelValues, timeDepRelDF diff --git a/avaframe/tests/test_com1DFA.py b/avaframe/tests/test_com1DFA.py index 49854b070..fe2e75075 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) @@ -1888,7 +1894,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): @@ -2147,6 +2153,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "True", "explicitFriction": 0, "timeDependentRelease": "False", + "timeDependentReleaseScenarios": "", } standardCfg["INPUT"] = { "entThThickness": "1.", @@ -2154,6 +2161,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "entThCi95": "None", "releaseScenario": "", "relThFile": "", + "timeDepRelCsv": "", } testDir = pathlib.Path(__file__).parents[0] @@ -2221,6 +2229,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "True", "explicitFriction": 0, "timeDependentRelease": "False", + "timeDependentReleaseScenarios": "", } testCfg["INPUT"] = { @@ -2228,6 +2237,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "entThId": "0", "entThCi95": "None", "releaseScenario": "relAlr", + "timeDepRelCsv": "", } testCfg["INPUT"]["DEM"] = "avaAlr.tif" testCfg["INPUT"]["relThFile"] = "" @@ -2332,6 +2342,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "True", "explicitFriction": 0, "timeDependentRelease": "False", + "timeDependentReleaseScenarios": "", } testCfg2["INPUT"] = { "entThThickness": "1.", @@ -2339,6 +2350,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"] = "" @@ -2417,6 +2429,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "entThCi95": "None", "releaseScenario": "", "relThFile": "", + "timeDepRelCsv": "", } testDir = pathlib.Path(__file__).parents[0] @@ -2428,6 +2441,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], @@ -2483,6 +2498,7 @@ def test_prepareVarSimDict(tmp_path, caplog): "dam": "False", "explicitFriction": 0, "timeDependentRelease": "True", + "timeDependentReleaseScenarios": "release1PF", } testCfg["INPUT"] = { diff --git a/avaframe/tests/test_getInput.py b/avaframe/tests/test_getInput.py index 3944101a1..034633926 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,9 +1584,8 @@ 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) 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: