Skip to content

Commit c3ffe25

Browse files
committed
fix: adding new table to LPF for thickness calculators
Also add option to ElementToDataframe to use headers columns provided and return a specific number of columns. These can be set using setncattr method for the group. If headers is included these will be used for the dataframe headers. If ncols is included this will return df.iloc[:,:ncols]
1 parent a6596f9 commit c3ffe25

5 files changed

Lines changed: 158 additions & 115 deletions

File tree

LoopProjectFile/ExtractedInformation.py

Lines changed: 114 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ def GetStratigraphicInformationGroup(rootGroup, verbose=False):
5151
)
5252

5353

54+
def GetStratigraphicThicknessGroup(rootGroup, verbose=False):
55+
resp = GetExtractedInformationGroup(rootGroup, verbose)
56+
if resp["errorFlag"]:
57+
return resp
58+
else:
59+
return LoopProjectFileUtils.GetGroup(resp["value"], "StratigraphicThickness", verbose)
60+
61+
5462
def GetDrillholeDescriptionGroup(rootGroup, verbose=False):
5563
resp = GetExtractedInformationGroup(rootGroup, verbose)
5664
if resp["errorFlag"]:
@@ -255,12 +263,9 @@ def GetDiscontinuityLog(root, indexList=[], indexRange=(0, 0), verbose=False):
255263
)
256264

257265

258-
259266
def SetStratigraphicLog(
260267
root,
261268
data,
262-
thickness_calculator_data=None,
263-
thickness_calculator_active_flags=None,
264269
append=False,
265270
verbose=False
266271
):
@@ -313,35 +318,13 @@ def SetStratigraphicLog(
313318
zlib=True,
314319
complevel=9,
315320
)
316-
317-
# Define compound types for thickness calculator and active flags (list of length 5)
318-
thicknessCalculatorType_t = siGroup.createCompoundType(
319-
LoopProjectFile.thicknessCalculatorType, "ThicknessCalculator"
320-
)
321-
siGroup.createVariable(
322-
"thicknessCalculator",
323-
thicknessCalculatorType_t,
324-
zlib=True,
325-
complevel=9,
326-
)
327321

328-
thicknessCalculatorActiveFlagsType_t = siGroup.createCompoundType(
329-
LoopProjectFile.thicknessCalculatorType, "ThicknessCalculatorActiveFlags"
330-
)
331-
siGroup.createVariable(
332-
"thicknessCalculatorActiveFlags",
333-
thicknessCalculatorActiveFlagsType_t,
334-
zlib=True,
335-
complevel=9,
336-
)
337322
else:
338323
siGroup = resp["value"]
339324

340325
if siGroup:
341326
stratigraphicLayersLocation = siGroup.variables["stratigraphicLayers"]
342-
thickness_calculator = siGroup.variables["thicknessCalculator"]
343-
thickness_calculator_active_flags_var = siGroup.variables["thicknessCalculatorActiveFlags"]
344-
327+
345328
index = 0
346329
if append:
347330
index = siGroup.dimensions["index"].size
@@ -350,112 +333,134 @@ def SetStratigraphicLog(
350333
index += 1
351334
siGroup.setncattr("index_MaxValid", index)
352335

353-
# Write thickness calculator data (must match expected structure)
354-
if thickness_calculator_data:
355-
for tc in thickness_calculator_data:
356-
if len(tc) != 5:
357-
errStr = "(ERROR) Each entry of thickness calculator data must be of length 5; this one is of length " + str(len(tc))
358-
if verbose:
359-
print(errStr)
360-
return {"errorFlag": True, "errorString": errStr}
361-
structured_data = numpy.array([thickness_calculator_data[0]], dtype=LoopProjectFile.thicknessCalculatorType)
362-
thickness_calculator[:] = structured_data[0]
363-
364-
# Write thickness calculator active flags (must match expected structure)
365-
if thickness_calculator_active_flags:
366-
for tcf in thickness_calculator_active_flags:
367-
if len(tcf) != 5:
368-
errStr = "(ERROR) Each entry of thickness calculator active flags must be of length 5; this one is of length " + str(len(tcf))
369-
if verbose:
370-
print(errStr)
371-
return {"errorFlag": True, "errorString": errStr}
372-
structured_active_flags = numpy.array([thickness_calculator_active_flags[0]], dtype=LoopProjectFile.thicknessCalculatorType)
373-
thickness_calculator_active_flags_var[:] = structured_active_flags[0]
374-
336+
375337
else:
376338
errStr = "(ERROR) Failed to create stratigraphic log group for strata setting"
377339
if verbose:
378340
print(errStr)
379341
response = {"errorFlag": True, "errorString": errStr}
380-
342+
381343
return response
382344

383-
def GetStratigraphicLog(
384-
root,
385-
indexList=[],
386-
indexRange=(0, 0),
387-
verbose=False
388-
):
345+
346+
def GetStratigraphicLog(root, indexList=[], indexRange=(0, 0), verbose=False):
389347
response = {"errorFlag": False}
390348
resp = GetStratigraphicInformationGroup(root)
391-
392349
if resp["errorFlag"]:
393-
return resp
394-
siGroup = resp["value"]
395-
data = []
396-
397-
try:
398-
# Get max valid index
399-
maxValidIndex = min(
400-
siGroup.dimensions["index"].size, siGroup.getncattr("index_MaxValid")
401-
)
402-
403-
# Check if variables exist in the group
404-
if "thicknessCalculator" not in siGroup.variables or "thicknessCalculatorActiveFlags" not in siGroup.variables:
405-
errStr = "Missing 'thicknessCalculator' or 'thicknessCalculatorActiveFlags' in the NetCDF file."
406-
if verbose:
407-
print(errStr)
408-
return {"errorFlag": True, "errorString": errStr}
409-
410-
# Fetch thickness calculator and active flags
411-
thickness_calculator_data = siGroup.variables["thicknessCalculator"][:]
412-
thickness_calculator_active_flags = siGroup.variables["thicknessCalculatorActiveFlags"][:]
413-
414-
# Select all layers
415-
if (
416-
not indexList
417-
and len(indexRange) == 2
418-
and indexRange == (0, 0)
419-
):
420-
for i in range(maxValidIndex):
421-
data.append(siGroup.variables["stratigraphicLayers"][i])
422-
423-
# Select based on list of indices
424-
elif indexList:
350+
response = resp
351+
else:
352+
siGroup = resp["value"]
353+
data = []
354+
maxValidIndex = min(siGroup.dimensions["index"].size, siGroup.getncattr("index_MaxValid"))
355+
# Select all option
356+
if indexList == [] and len(indexRange) == 2 and indexRange[0] == 0 and indexRange[1] == 0:
357+
# Select all
358+
for i in range(0, maxValidIndex):
359+
data.append((siGroup.variables.get("stratigraphicLayers")[i]))
360+
response["value"] = data
361+
# Select based on list of indices option
362+
elif indexList != []:
425363
for i in indexList:
426-
if 0 <= int(i) < maxValidIndex:
427-
data.append(siGroup.variables["stratigraphicLayers"][i])
428-
429-
# Select based on index range
430-
elif (
431-
len(indexRange) == 2
432-
and 0 <= indexRange[0] < indexRange[1] <= maxValidIndex
433-
):
364+
if int(i) >= 0 and int(i) < maxValidIndex:
365+
data.append((siGroup.variables.get("stratigraphicLayers")[i]))
366+
response["value"] = data
367+
# Select based on indices range option
368+
elif len(indexRange) == 2 and indexRange[0] >= 0 and indexRange[1] >= indexRange[0]:
434369
for i in range(indexRange[0], indexRange[1]):
435-
data.append(siGroup.variables["stratigraphicLayers"][i])
436-
370+
if int(i) >= 0 and int(i) < maxValidIndex:
371+
data.append((siGroup.variables.get("stratigraphicLayers")[i]))
372+
response["value"] = data
437373
else:
438-
errStr = "Invalid filter option for stratigraphic log retrieval."
374+
errStr = "Non-implemented filter option"
439375
if verbose:
440376
print(errStr)
441-
return {"errorFlag": True, "errorString": errStr}
377+
response = {"errorFlag": True, "errorString": errStr}
378+
return response
442379

443-
# Construct response
444-
response["value"] = {
445-
"stratigraphicLayers": data,
446-
"thicknessCalculatorData": thickness_calculator_data.tolist(),
447-
"thicknessCalculatorActiveFlags": thickness_calculator_active_flags.tolist(),
448-
}
449380

450-
except Exception as e:
451-
errStr = f"(ERROR) Exception during stratigraphic log retrieval: {str(e)}"
381+
def SetStratigraphicThicknesses(root, data, headers=None,ncols=None,append=False, verbose=False):
382+
response = {"errorFlag": False}
383+
resp = GetExtractedInformationGroup(root)
384+
if resp["errorFlag"]:
385+
# Create Extracted Information Group as it doesn't exist
386+
eiGroup = root.createGroup("ExtractedInformation")
387+
else:
388+
eiGroup = resp["value"]
389+
390+
resp = GetStratigraphicThicknessGroup(root)
391+
if resp["errorFlag"]:
392+
stGroup = eiGroup.createGroup("StratigraphicThickness")
393+
stGroup.setncattr("index_MaxValid", -1)
394+
stGroup.createDimension("index", None)
395+
stratigraphicThicknessType_t = stGroup.createCompoundType(
396+
LoopProjectFile.stratigraphicThicknessType, "stratigraphicThickness"
397+
)
398+
stGroup.createVariable(
399+
"stratigraphicThicknesses",
400+
stratigraphicThicknessType_t,
401+
("index"),
402+
zlib=True,
403+
complevel=9,
404+
)
405+
else:
406+
stGroup = resp["value"]
407+
408+
if stGroup:
409+
stratigraphicThicknesses = stGroup.variables["stratigraphicThicknesses"]
410+
if headers:
411+
stGroup.setncattr("headers", headers)
412+
if ncols:
413+
stGroup.setncattr("ncols", ncols)
414+
index = 0
415+
if append:
416+
index = stGroup.dimensions["index"].size
417+
for i in data:
418+
stratigraphicThicknesses[index] = i
419+
index += 1
420+
stGroup.setncattr("index_MaxValid", index)
421+
else:
422+
errStr = "(ERROR) Failed to create stratigraphic log group for strata setting"
452423
if verbose:
453424
print(errStr)
454-
return {"errorFlag": True, "errorString": errStr}
425+
response = {"errorFlag": True, "errorString": errStr}
455426

456427
return response
457428

458429

430+
def GetStratigraphicThicknesses(root, indexList=[], indexRange=(0, 0), verbose=False):
431+
response = {"errorFlag": False}
432+
resp = GetStratigraphicThicknessGroup(root)
433+
if resp["errorFlag"]:
434+
response = resp
435+
else:
436+
siGroup = resp["value"]
437+
data = []
438+
maxValidIndex = min(siGroup.dimensions["index"].size, siGroup.getncattr("index_MaxValid"))
439+
# Select all option
440+
response['attributes'] = {a:siGroup.getncattr(a) for a in siGroup.ncattrs()}
441+
if indexList == [] and len(indexRange) == 2 and indexRange[0] == 0 and indexRange[1] == 0:
442+
# Select all
443+
for i in range(0, maxValidIndex):
444+
data.append((siGroup.variables.get("stratigraphicThicknesses")[i]))
445+
response["value"] = data
446+
# Select based on list of indices option
447+
elif indexList != []:
448+
for i in indexList:
449+
if int(i) >= 0 and int(i) < maxValidIndex:
450+
data.append((siGroup.variables.get("stratigraphicThicknesses")[i]))
451+
response["value"] = data
452+
# Select based on indices range option
453+
elif len(indexRange) == 2 and indexRange[0] >= 0 and indexRange[1] >= indexRange[0]:
454+
for i in range(indexRange[0], indexRange[1]):
455+
if int(i) >= 0 and int(i) < maxValidIndex:
456+
data.append((siGroup.variables.get("stratigraphicThicknesses")[i]))
457+
response["value"] = data
458+
else:
459+
errStr = "Non-implemented filter option"
460+
if verbose:
461+
print(errStr)
462+
response = {"errorFlag": True, "errorString": errStr}
463+
return response
459464

460465

461466
# Set drillhole log

LoopProjectFile/LoopProjectFile.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,10 @@ def Set(filename, element, **kwargs):
286286
response = ExtractedInformation.SetStratigraphicLog(
287287
root, append=True, **kwargs
288288
)
289-
289+
elif element == "stratigraphicThicknesses":
290+
response = ExtractedInformation.SetStratigraphicThicknesses(root, **kwargs)
291+
elif element == "stratigraphicThicknessCalculatorLabels":
292+
response = ExtractedInformation.SetStratigraphicThicknessCalculatorLabels(root, **kwargs)
290293

291294

292295
elif element == "faultLog":
@@ -426,6 +429,10 @@ def Get(filename, element, **kwargs):
426429
response = DataCollection.GetDrillholeProperties(root, **kwargs)
427430
elif element == "stratigraphicLog":
428431
response = ExtractedInformation.GetStratigraphicLog(root, **kwargs)
432+
elif element == "stratigraphicThicknesses":
433+
response = ExtractedInformation.GetStratigraphicThicknesses(root, **kwargs)
434+
elif element == "stratigraphicThicknessCalculatorLabels":
435+
response = ExtractedInformation.GetStratigraphicThicknessCalculatorLabels(root, **kwargs)
429436
elif element == "faultLog":
430437
response = ExtractedInformation.GetFaultLog(root, **kwargs)
431438
elif element == "foldLog":
@@ -733,9 +740,9 @@ def CheckFileValid(filename, verbose=False):
733740
("group", "S120"),
734741
("supergroup", "S120"),
735742
("enabled", "u1"),
736-
("ThicknessMean", "<f8", (5,)),
737-
("ThicknessMedian", "<f8", (5,)),
738-
("ThicknessStdDev", "<f8", (5,)),
743+
("ThicknessMean", "<f8"),
744+
("ThicknessMedian", "<f8"),
745+
("ThicknessStdDev", "<f8"),
739746
("colour1Red", "u1"),
740747
("colour1Green", "u1"),
741748
("colour1Blue", "u1"),
@@ -744,7 +751,26 @@ def CheckFileValid(filename, verbose=False):
744751
("colour2Blue", "u1"),
745752
]
746753
)
747-
754+
stratigraphicThicknessType = numpy.dtype(
755+
[
756+
('name', 'S120'),
757+
('thickness1_mean', '<f8'),
758+
('thickness1_median', '<f8'),
759+
('thickness1_stddev', '<f8'),
760+
('thickness2_mean', '<f8'),
761+
('thickness2_median', '<f8'),
762+
('thickness2_stddev', '<f8'),
763+
('thickness3_mean', '<f8'),
764+
('thickness3_median', '<f8'),
765+
('thickness3_stddev', '<f8'),
766+
('thickness4_mean', '<f8'),
767+
('thickness4_median', '<f8'),
768+
('thickness4_stddev', '<f8'),
769+
('thickness5_mean', '<f8'),
770+
('thickness5_median', '<f8'),
771+
('thickness5_stddev', '<f8'),
772+
]
773+
)
748774
thicknessCalculatorType = numpy.dtype([
749775
("name1", "S120"),
750776
("name2", "S120"),
@@ -833,4 +859,4 @@ def ConvertToDataFrame(data, loopCompoundType):
833859
columns = list(loopCompoundType.names)
834860
df = pandas.DataFrame.from_records(data, columns=columns)
835861
df = df.applymap(lambda x: x.decode() if isinstance(x, bytes) else x)
836-
return df
862+
return df

LoopProjectFile/LoopProjectFileUtils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,11 +304,21 @@ def ElementToDataframe(loopFilename, element, loopCompoundType):
304304
return None
305305
else:
306306
columns = list(loopCompoundType.names)
307+
attr = resp.get("attributes",{})
308+
307309
df = pandas.DataFrame.from_records(resp["value"], columns=columns)
310+
308311
for name in columns:
309312
if type(loopCompoundType[name]) is not numpy.dtypes.VoidDType:
310313
df[name] = df[name].astype(loopCompoundType[name])
311314
df = df.map(lambda x: x.decode() if isinstance(x, bytes) else x)
315+
if "headers" in attr:
316+
if len(attr["headers"]) != len(columns):
317+
print("Number of headers does not match number of columns")
318+
else:
319+
df = df.rename(columns={c:c2 for c,c2 in zip(columns,attr["headers"])})
320+
if "ncols" in attr:
321+
df = df.iloc[:, : attr["ncols"]]
312322
# df.set_index(columns[0], inplace=True)
313323
return df # .to_csv(outputFilename)
314324

LoopProjectFile/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
foliationObservationType, # noqa: F401
1515
discontinuityObservationType, # noqa: F401
1616
stratigraphicLayerType, # noqa: F401
17+
stratigraphicThicknessType, # noqa: F401
1718
stratigraphicObservationType, # noqa: F401
1819
contactObservationType, # noqa: F401
1920
eventRelationshipType, # noqa: F401

LoopProjectFile/projectfile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"stratigraphicObservations": LoopProjectFile.stratigraphicObservationType,
2222
"contacts": LoopProjectFile.contactObservationType,
2323
"stratigraphicLog": LoopProjectFile.stratigraphicLayerType,
24+
"stratigraphicThicknesses": LoopProjectFile.stratigraphicThicknessType,
2425
"faultLog": LoopProjectFile.faultEventType,
2526
"foldLog": None,
2627
"foliationLog": None,

0 commit comments

Comments
 (0)