From da6595d7f6408c449ade78b60b51725c995cb469 Mon Sep 17 00:00:00 2001 From: SAYANTANDE Date: Wed, 17 Jul 2024 06:15:00 +0530 Subject: [PATCH 1/8] Added parse_log function and test cases for model fitting data extraction --- src/parse_log.jl | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/parse_log.jl diff --git a/src/parse_log.jl b/src/parse_log.jl new file mode 100644 index 00000000..0c6b0692 --- /dev/null +++ b/src/parse_log.jl @@ -0,0 +1,42 @@ +# parse_log.jl + +""" + parse_log(log) + +Parse a log of actions and extract model fitting entries into a dictionary. + +# Arguments +- `log`: A list of tuples representing log entries. + +# Returns +A dictionary where keys are model names and values are lists of dictionaries, +each containing model parameters, error, and index from the log. + +# Example +```julia +data.log = [...] # Define your log data +models_data = parse_log(data.log) +println(models_data) + +""" +function parse_log(log) + models_dict = Dict{String, Vector{Dict{String, Any}}}() + + for (idx, entry) in enumerate(log) + if entry.action.funct == :modelfit + model_name = entry.info.model_name + model_params = entry.info.model_params + error = entry.info.error + + model_entry = Dict("params" => model_params, "error" => error, "index" => idx) + + if haskey(models_dict, model_name) + push!(models_dict[model_name], model_entry) + else + models_dict[model_name] = [model_entry] + end + end + end + + return models_dict +end From d25fa8fbdf7e1663032a0ab87011582b0f2365d6 Mon Sep 17 00:00:00 2001 From: SAYANTANDE Date: Wed, 17 Jul 2024 06:27:02 +0530 Subject: [PATCH 2/8] Add parse_log function and corresponding tests --- test/test_parse_log.jl | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/test_parse_log.jl diff --git a/test/test_parse_log.jl b/test/test_parse_log.jl new file mode 100644 index 00000000..9e4ee92d --- /dev/null +++ b/test/test_parse_log.jl @@ -0,0 +1,33 @@ +using Test + +include("parse_log.jl") + +# Mock log data for testing +const mock_log = [ + (action = (type = :source, funct = :importcsv), info = ()), + (action = (type = :analysis, funct = :modelfit), info = (model_name = "maxwell", model_params = Dict("η" => 76.30267813573391, "k" => 0.5451389878296595), error = 4.234991209005228)), + (action = (type = :analysis, funct = :modelfit), info = (model_name = "SLS_Zener", model_params = Dict("η" => 0.2995472252368927, "kᵦ" => 1.0333390861749647, "kᵧ" => 0.5), error = 3.134312236497204e-11)), +] + +# Define the test function +function test_parse_log() + expected_output = Dict( + "maxwell" => [ + Dict("params" => Dict("η" => 76.30267813573391, "k" => 0.5451389878296595), "error" => 4.234991209005228, "index" => 2), + ], + "SLS_Zener" => [ + Dict("params" => Dict("η" => 0.2995472252368927, "kᵦ" => 1.0333390861749647, "kᵧ" => 0.5), "error" => 3.134312236497204e-11, "index" => 3), + ] + ) + + actual_output = parse_log(mock_log) + + @test actual_output == expected_output +end + +# Run tests +@testset "Tests for parse_log function" begin + @testset "Basic functionality" begin + test_parse_log() + end +end From 29ccdfbbb426cba2ba47a67140725f2c806893c3 Mon Sep 17 00:00:00 2001 From: SAYANTANDE Date: Mon, 22 Jul 2024 01:24:06 +0530 Subject: [PATCH 3/8] to define and call the new function and test fnuction --- src/RHEOS.jl | 1 + src/rheodata.jl | 36 ++++++++++++++++++++++ test/rheodata.jl | 77 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/src/RHEOS.jl b/src/RHEOS.jl index a682d001..770b9dda 100644 --- a/src/RHEOS.jl +++ b/src/RHEOS.jl @@ -36,6 +36,7 @@ const RheovecOrNone = Union{Vector{RheoFloat}, Nothing} ###################################################################### export namedtuple, dict export RheoFloat +export extractfitdata # definitions.jl export RheoLogItem, RheoLog, rheologrun, showlog diff --git a/src/rheodata.jl b/src/rheodata.jl index cac14cc1..f7a56f6d 100644 --- a/src/rheodata.jl +++ b/src/rheodata.jl @@ -704,6 +704,42 @@ end +""" + extractfitdata(log) +Parse a log of actions and extract model fitting entries into a dictionary. +# Arguments +- `log`: A list of tuples representing log entries. +# Returns +A dictionary where keys are model names and values are lists of named tuples, +each containing model parameters, error, info, and index from the log. +# Example +```julia +data.log = [...] # Define your log data +models_data = extractfitdata(data.log) +println(models_data) +""" +function extractfitdata(log) + models_dict = Dict{String, Vector{NamedTuple{(:params, :info, :index), Tuple{Any, Any, Int}}}}() + + for (idx, entry) in enumerate(log) + if entry.action.funct == :modelfit + model_name = entry.info.model_name + model_params = entry.info.model_params + error = entry.info.error + info = entry.info + + model_entry = (params = model_params, info = info, index = idx) + + if haskey(models_dict, model_name) + push!(models_dict[model_name], model_entry) + else + models_dict[model_name] = [model_entry] + end + end + end + + return models_dict +end diff --git a/test/rheodata.jl b/test/rheodata.jl index c16a5d7c..1f01ed70 100644 --- a/test/rheodata.jl +++ b/test/rheodata.jl @@ -128,3 +128,80 @@ function __mapdata!() end @test __mapdata!() + +using RHEOS + +# Step 1: Generate Timeline +datat = timeline(t_start = 0, t_end = 20.0, step = 0.02) # Create a timeline from 0 to 20 seconds with a step size of 0.02 seconds +rheotimedatatype(datat) # Ensure the timeline data type is correct for RHEOS + +# Step 2: Generate Strain Data (Ramp & hold) +dramp_strain = strainfunction(datat, ramp(offset = 1.0, gradient = 0.8)) # Generate a ramp strain function with offset 1.0 and gradient 0.8 +dhold_strain = dramp_strain - strainfunction(datat, ramp(offset = 3.0, gradient = 0.8)) # Generate a hold strain function by subtracting a shifted ramp + +# Step 3: Generate Stress Data (Ramp & hold) +dramp_stress = stressfunction(datat, ramp(offset = 4.0, gradient = 0.8)) # Generate a ramp stress function with offset 4.0 and gradient 0.8 +dhold_stress = dramp_stress - stressfunction(datat, ramp(offset = 5.0, gradient = 0.8)) # Generate a hold stress function by subtracting a shifted ramp + +# Define the rheological model +model = RheoModel(SLS_Zener, (η = 1, kᵦ = 1, kᵧ = 1)) +data_ext = dhold_stress +rheotimedatatype(data_ext) +SLS_predict = modelpredict(data_ext, model) +data = SLS_predict + +# Fit the model to the data +SLS_Zener_model = modelfit(data, SLS_Zener, strain_imposed) + +# Define the test function +function _test_extractfitdata() + # Call the extractfitdata function with data.log + extracted_data = extractfitdata(data.log) + all_tests_passed = true + + # Iterate through data.log to dynamically verify modelfit entries + for (index, log_entry) in enumerate(data.log) + if log_entry.action.funct == :modelfit + model_name = log_entry.info.model_name + expected_params = log_entry.info.model_params + expected_info = log_entry.info + + if haskey(extracted_data, model_name) + model_entries = extracted_data[model_name] + + # Find the corresponding entry in the extracted data + matching_entries = filter(x -> x.index == index, model_entries) + + if length(matching_entries) == 1 + extracted_entry = matching_entries[1] + + if extracted_entry.params == expected_params && extracted_entry.info == expected_info && extracted_entry.index == index + println("Test passed for entry $index: Extracted data matches expected data.") + else + println("Test failed for entry $index: Extracted data does not match expected data.") + println("Extracted params: $(extracted_entry.params)") + println("Expected params: $expected_params") + println("Extracted info: $(extracted_entry.info)") + println("Expected info: $expected_info") + println("Extracted index: $(extracted_entry.index)") + println("Expected index: $index") + all_tests_passed = false + end + else + println("Test failed for entry $index: Number of matching entries does not match expected.") + println("Matching entries: $matching_entries") + all_tests_passed = false + end + else + println("Test failed for entry $index: $model_name model not found in extracted data.") + println("Extracted data: $extracted_data") + all_tests_passed = false + end + end + end + + return all_tests_passed +end + +# Run the test function +@test _test_extractfitdata() From 708debb6ffc9fa851211e3424b71eb55773817f7 Mon Sep 17 00:00:00 2001 From: SAYANTAN Date: Mon, 22 Jul 2024 02:02:03 +0530 Subject: [PATCH 4/8] Delete test/test_parse_log.jl --- test/test_parse_log.jl | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 test/test_parse_log.jl diff --git a/test/test_parse_log.jl b/test/test_parse_log.jl deleted file mode 100644 index 9e4ee92d..00000000 --- a/test/test_parse_log.jl +++ /dev/null @@ -1,33 +0,0 @@ -using Test - -include("parse_log.jl") - -# Mock log data for testing -const mock_log = [ - (action = (type = :source, funct = :importcsv), info = ()), - (action = (type = :analysis, funct = :modelfit), info = (model_name = "maxwell", model_params = Dict("η" => 76.30267813573391, "k" => 0.5451389878296595), error = 4.234991209005228)), - (action = (type = :analysis, funct = :modelfit), info = (model_name = "SLS_Zener", model_params = Dict("η" => 0.2995472252368927, "kᵦ" => 1.0333390861749647, "kᵧ" => 0.5), error = 3.134312236497204e-11)), -] - -# Define the test function -function test_parse_log() - expected_output = Dict( - "maxwell" => [ - Dict("params" => Dict("η" => 76.30267813573391, "k" => 0.5451389878296595), "error" => 4.234991209005228, "index" => 2), - ], - "SLS_Zener" => [ - Dict("params" => Dict("η" => 0.2995472252368927, "kᵦ" => 1.0333390861749647, "kᵧ" => 0.5), "error" => 3.134312236497204e-11, "index" => 3), - ] - ) - - actual_output = parse_log(mock_log) - - @test actual_output == expected_output -end - -# Run tests -@testset "Tests for parse_log function" begin - @testset "Basic functionality" begin - test_parse_log() - end -end From 318b67c74be5b8bcb8cdd65dceb1bb8210b8e33e Mon Sep 17 00:00:00 2001 From: SAYANTAN Date: Mon, 22 Jul 2024 02:02:28 +0530 Subject: [PATCH 5/8] Delete src/parse_log.jl --- src/parse_log.jl | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 src/parse_log.jl diff --git a/src/parse_log.jl b/src/parse_log.jl deleted file mode 100644 index 0c6b0692..00000000 --- a/src/parse_log.jl +++ /dev/null @@ -1,42 +0,0 @@ -# parse_log.jl - -""" - parse_log(log) - -Parse a log of actions and extract model fitting entries into a dictionary. - -# Arguments -- `log`: A list of tuples representing log entries. - -# Returns -A dictionary where keys are model names and values are lists of dictionaries, -each containing model parameters, error, and index from the log. - -# Example -```julia -data.log = [...] # Define your log data -models_data = parse_log(data.log) -println(models_data) - -""" -function parse_log(log) - models_dict = Dict{String, Vector{Dict{String, Any}}}() - - for (idx, entry) in enumerate(log) - if entry.action.funct == :modelfit - model_name = entry.info.model_name - model_params = entry.info.model_params - error = entry.info.error - - model_entry = Dict("params" => model_params, "error" => error, "index" => idx) - - if haskey(models_dict, model_name) - push!(models_dict[model_name], model_entry) - else - models_dict[model_name] = [model_entry] - end - end - end - - return models_dict -end From 0def444d1a66e008b768632e0a77d82092c078a9 Mon Sep 17 00:00:00 2001 From: SAYANTAN Date: Mon, 22 Jul 2024 17:46:40 +0530 Subject: [PATCH 6/8] Update rheodata.jl --- src/rheodata.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rheodata.jl b/src/rheodata.jl index f7a56f6d..11b9a995 100644 --- a/src/rheodata.jl +++ b/src/rheodata.jl @@ -705,20 +705,20 @@ end """ - extractfitdata(log) -Parse a log of actions and extract model fitting entries into a dictionary. + extractfitdata(data) +Parse a log of actions from a `RheoTimeData` instance and extract model fitting entries into a dictionary. # Arguments -- `log`: A list of tuples representing log entries. +- `data`: A `RheoTimeData` instance containing the log of actions. # Returns A dictionary where keys are model names and values are lists of named tuples, each containing model parameters, error, info, and index from the log. # Example ```julia data.log = [...] # Define your log data -models_data = extractfitdata(data.log) +models_data = extractfitdata(data) println(models_data) """ -function extractfitdata(log) +function extractfitdata(data) models_dict = Dict{String, Vector{NamedTuple{(:params, :info, :index), Tuple{Any, Any, Int}}}}() for (idx, entry) in enumerate(log) From 015ed64793f6ee004841f68e83521843bc7f7ad3 Mon Sep 17 00:00:00 2001 From: SAYANTAN Date: Mon, 22 Jul 2024 17:57:34 +0530 Subject: [PATCH 7/8] Update rheodata.jl --- test/rheodata.jl | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/test/rheodata.jl b/test/rheodata.jl index 1f01ed70..556d46b2 100644 --- a/test/rheodata.jl +++ b/test/rheodata.jl @@ -129,36 +129,26 @@ end @test __mapdata!() -using RHEOS - -# Step 1: Generate Timeline -datat = timeline(t_start = 0, t_end = 20.0, step = 0.02) # Create a timeline from 0 to 20 seconds with a step size of 0.02 seconds -rheotimedatatype(datat) # Ensure the timeline data type is correct for RHEOS - -# Step 2: Generate Strain Data (Ramp & hold) -dramp_strain = strainfunction(datat, ramp(offset = 1.0, gradient = 0.8)) # Generate a ramp strain function with offset 1.0 and gradient 0.8 -dhold_strain = dramp_strain - strainfunction(datat, ramp(offset = 3.0, gradient = 0.8)) # Generate a hold strain function by subtracting a shifted ramp +function _test_extractfitdata() + # Step 1: Generate Timeline + datat = timeline(t_start = 0, t_end = 20.0, step = 0.02) # Create a timeline from 0 to 20 seconds with a step size of 0.02 seconds -# Step 3: Generate Stress Data (Ramp & hold) -dramp_stress = stressfunction(datat, ramp(offset = 4.0, gradient = 0.8)) # Generate a ramp stress function with offset 4.0 and gradient 0.8 -dhold_stress = dramp_stress - stressfunction(datat, ramp(offset = 5.0, gradient = 0.8)) # Generate a hold stress function by subtracting a shifted ramp + # Step 2: Generate Stress Data (Ramp & hold) + dramp_stress = stressfunction(datat, ramp(offset = 4.0, gradient = 0.8)) # Generate a ramp stress function with offset 4.0 and gradient 0.8 + dhold_stress = dramp_stress - stressfunction(datat, ramp(offset = 5.0, gradient = 0.8)) # Generate a hold stress function by subtracting a shifted ramp -# Define the rheological model -model = RheoModel(SLS_Zener, (η = 1, kᵦ = 1, kᵧ = 1)) -data_ext = dhold_stress -rheotimedatatype(data_ext) -SLS_predict = modelpredict(data_ext, model) -data = SLS_predict + # Define the rheological model + model = RheoModel(SLS_Zener, (η = 1, kᵦ = 1, kᵧ = 1)) + SLS_predict = modelpredict(dhold_stress, model) + data = SLS_predict -# Fit the model to the data -SLS_Zener_model = modelfit(data, SLS_Zener, strain_imposed) + # Fit the model to the data + SLS_Zener_model = modelfit(data, SLS_Zener, strain_imposed) -# Define the test function -function _test_extractfitdata() - # Call the extractfitdata function with data.log - extracted_data = extractfitdata(data.log) + # Call the extractfitdata function with data + extracted_data = extractfitdata(data) all_tests_passed = true - + # Iterate through data.log to dynamically verify modelfit entries for (index, log_entry) in enumerate(data.log) if log_entry.action.funct == :modelfit @@ -199,9 +189,9 @@ function _test_extractfitdata() end end end - + return all_tests_passed end # Run the test function -@test _test_extractfitdata() +_test_extractfitdata() From 744e75834cfb2f55008f7bf6c434e5951fa91921 Mon Sep 17 00:00:00 2001 From: SAYANTAN Date: Mon, 22 Jul 2024 18:52:25 +0530 Subject: [PATCH 8/8] Update examples.jl --- docs/src/examples.jl | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/docs/src/examples.jl b/docs/src/examples.jl index 3a0ceccf..90f317db 100644 --- a/docs/src/examples.jl +++ b/docs/src/examples.jl @@ -99,3 +99,67 @@ ax.set_xlabel("Time") ax.set_ylabel("Stress") ax.grid("on") #!nb fig #hide + +# ## Example 4 + +# This example demonstrates generating a timeline and stress data, fitting multiple models to the data, +# calling the `extractfitdata` function, listing the errors, and determining which model fits the best. + +using RHEOS + +# Generate Timeline +datat = timeline(t_start = 0, t_end = 20.0, step = 0.02) # Create a timeline from 0 to 20 seconds with a step size of 0.02 seconds + +# Generate Stress Data (Ramp & hold) +dramp_stress = stressfunction(datat, ramp(offset = 4.0, gradient = 0.8)) # Generate a ramp stress function with offset 4.0 and gradient 0.8 +dhold_stress = dramp_stress - stressfunction(datat, ramp(offset = 5.0, gradient = 0.8)) # Generate a hold stress function by subtracting a shifted ramp + +# Define the rheological model and predict +model = RheoModel(SLS_Zener, (η = 1, kᵦ = 1, kᵧ = 1)) +SLS_predict = modelpredict(dhold_stress, model) +data = SLS_predict + +# Fit three models to the data +SLS_Zener_model = modelfit(data, SLS_Zener, strain_imposed) +Maxwell_model = modelfit(data, Maxwell, strain_imposed) +BurgersLiquid_model = modelfit(data, BurgersLiquid, strain_imposed) + +# Call the extractfitdata function to extract fitting data +extracted_data = extractfitdata(data) + +# Determine which model fits best by comparing errors +best_model = "" +min_error = Inf + +for (model_name, entries) in extracted_data + total_error = sum(entry.info.error for entry in entries) + + println("Model: $model_name, Total Error: $total_error") + + if total_error < min_error + min_error = total_error + best_model = model_name + end +end + +println("Best fitting model: $best_model with total error: $min_error") + +# Create strain-only data for model predictions +strain_only_data = onlystrain(data) + +# Get model predictions for plotting +SLS_Zener_predict = modelpredict(strain_only_data, SLS_Zener_model) +Maxwell_predict = modelpredict(strain_only_data, Maxwell_model) +BurgersLiquid_predict = modelpredict(strain_only_data, BurgersLiquid_model) + +# Plot data and fitted models +fig, ax = subplots(1, 1, figsize = (7, 5)) +ax.plot(data.t, data.σ, ".", color = "green", label = "Original Data") +ax.plot(SLS_Zener_predict.t, SLS_Zener_predict.σ, "-", color = "red", label = "SLS_Zener Model") +ax.plot(Maxwell_predict.t, Maxwell_predict.σ, "--", color = "blue", label = "Maxwell Model") +ax.plot(BurgersLiquid_predict.t, BurgersLiquid_predict.σ, ":", color = "purple", label = "BurgersLiquid Model") +ax.set_xlabel("Time") +ax.set_ylabel("Stress") +ax.legend() +ax.grid("on") +#!nb fig #hide