return Generate_Face_Acidobazic(Assignment, Answer_Message, Answer_Code, Parameters, HTML, Pr_ID, Pr_Cat);
elsif Problem_Type_Str = PROBLEM_TYPE_SOLUBILITY then
return Generate_Face_Solubility(Assignment, Answer_Message, Answer_Code, Parameters, HTML, Pr_ID, Pr_Cat);
+ elsif Problem_Type_Str = PROBLEM_TYPE_TITRATION_CURVE then
+ return Generate_Face_Titration_Curve(Assignment, Answer_Message, Answer_Code, Parameters, HTML, Pr_ID, Pr_Cat);
else
return E_INVAL;
end if;
return OK;
end Generate_Face_Solubility;
+
+ function Generate_Face_Titration_Curve(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map;
+ Answer_Message: in UB_Text;
+ Answer_Code: in Problem_Generator_Syswides.Answer_RetCode;
+ Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
+ HTML: out HTML_Code; Pr_ID: in String; Pr_Cat: in String) return RetCode is
+ use AWS.Templates;
+ use Problem_Generator_Syswides;
+ use Problem_Generator_Syswides.Assignment_Info;
+ use Problem_Generator_Syswides.Parameters_Info;
+
+ Translations_Hdr: Translate_Set;
+ Translations: Translate_Set;
+ Translations_Answer: Translate_Set;
+ Temp: HTML_Code;
+ begin
+ Insert(Translations_Hdr, Assoc(HEADER_CAPTION_KEY, "< " & Titration_Curve_Suite.PROBLEM_NAME_READABLE));
+ Insert(Translations_Hdr, Assoc(META_EXPIRE_NOW_KEY, META_EXPIRE_NOW_TEXT));
+ Insert(Translations_Hdr, Assoc(META_NO_CACHE_KEY, META_NO_CACHE_TEXT));
+ HTML := Parse(Filename => "templates/header.html", Translations => Translations_Hdr);
+ -- Add JavaScripts
+ Temp := Parse(Filename => "scripts/expand_collapse.js", Cached => True);
+ Append_HTML(Source => HTML, New_Item => Temp);
+
+ -- Mandatory hidden parameters
+ Insert(Translations, Assoc(RESERVED_PROBLEM_ID_KEY, RESERVED_PROBLEM_ID_KEY));
+ Insert(Translations, Assoc(RESERVED_PROBLEM_ID_VAL_KEY, Pr_ID));
+ Insert(Translations, Assoc(RESERVED_PROBLEM_CATEGORY_KEY, RESERVED_PROBLEM_CATEGORY_KEY));
+ Insert(Translations, Assoc(RESERVED_PROBLEM_CATEGORY_VAL_KEY, Pr_Cat));
+ -- Assignment
+ Insert(Translations, Assoc(Titration_Curve_Suite.SAMPLE_CONC_INT_KEY, Assignment.Element(Titration_Curve_Suite.SAMPLE_CONC_INT_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.SAMPLE_CONC_DEC_KEY, Assignment.Element(Titration_Curve_Suite.SAMPLE_CONC_DEC_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.SAMPLE_CONC_EXP_KEY, Assignment.Element(Titration_Curve_Suite.SAMPLE_CONC_EXP_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.SAMPLE_TYPE_KEY, Assignment.Element(Titration_Curve_Suite.SAMPLE_TYPE_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.TITRANT_TYPE_KEY, Assignment.Element(Titration_Curve_Suite.TITRANT_TYPE_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.SAMPLE_VOLUME_INT_KEY, Assignment.Element(Titration_Curve_Suite.SAMPLE_VOLUME_INT_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.SAMPLE_VOLUME_DEC_KEY, Assignment.Element(Titration_Curve_Suite.SAMPLE_VOLUME_DEC_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.SAMPLE_VOLUME_EXP_KEY, Assignment.Element(Titration_Curve_Suite.SAMPLE_VOLUME_EXP_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.TITRANT_CONC_INT_KEY, Assignment.Element(Titration_Curve_Suite.TITRANT_CONC_INT_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.TITRANT_CONC_DEC_KEY, Assignment.Element(Titration_Curve_Suite.TITRANT_CONC_DEC_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.TITRANT_CONC_EXP_KEY, Assignment.Element(Titration_Curve_Suite.TITRANT_CONC_EXP_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.PKX1_INT_KEY, Assignment.Element(Titration_Curve_Suite.PKX1_INT_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.PKX1_DEC_KEY, Assignment.Element(Titration_Curve_Suite.PKX1_DEC_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.PKX2_INT_KEY, Assignment.Element(Titration_Curve_Suite.PKX2_INT_KEY)));
+ Insert(Translations, Assoc(Titration_Curve_Suite.PKX2_DEC_KEY, Assignment.Element(Titration_Curve_Suite.PKX2_DEC_KEY)));
+ --
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_PH_START_KEY, Titration_Curve_Suite.ANSWER_PH_START_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_VOLUME_START_KEY, Titration_Curve_Suite.ANSWER_VOLUME_FIRST_HALF_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_PH_FIRST_HALF_KEY, Titration_Curve_Suite.ANSWER_PH_FIRST_HALF_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_VOLUME_FIRST_HALF_KEY, Titration_Curve_Suite.ANSWER_VOLUME_FIRST_HALF_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_PH_FIRST_EQUIV_KEY, Titration_Curve_Suite.ANSWER_PH_FIRST_EQUIV_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_VOLUME_FIRST_EQUIV_KEY, Titration_Curve_Suite.ANSWER_VOLUME_FIRST_EQUIV_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_PH_SECOND_HALF_KEY, Titration_Curve_Suite.ANSWER_PH_SECOND_HALF_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_VOLUME_SECOND_HALF_KEY, Titration_Curve_Suite.ANSWER_VOLUME_SECOND_HALF_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_PH_SECOND_EQUIV_KEY, Titration_Curve_Suite.ANSWER_PH_SECOND_EQUIV_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_VOLUME_SECOND_EQUIV_KEY, Titration_Curve_Suite.ANSWER_VOLUME_SECOND_EQUIV_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_PH_OVER_SECOND_EQUIV_KEY, Titration_Curve_Suite.ANSWER_PH_OVER_SECOND_EQUIV_KEY));
+ Insert(Translations, Assoc(Titration_Curve_Suite.ANSWER_VOLUME_OVER_SECOND_EQUIV_KEY, Titration_Curve_Suite.ANSWER_VOLUME_OVER_SECOND_EQUIV_KEY));
+
+ case Answer_Code is
+ when Correct_Answer =>
+ Insert(Translations_Answer, Assoc(ANSWER_KIND_KEY, ANSWER_KIND_GOOD));
+ Insert(Translations_Answer, Assoc(ANSWER_MESSAGE_KEY, UB_Text_To_Fixed_String(Answer_Message)));
+ Insert(Translations_Answer, Assoc(Titration_Curve_Suite.TITRATION_CURVE_IMAGE_PATH_KEY, Assignment.Element(Titration_Curve_Suite.TITRATION_CURVE_IMAGE_PATH_KEY)));
+ Temp := Parse(Filename => "templates/titration_curve_answer_section.html", Translations => Translations_Answer);
+ Insert(Translations, Assoc(ANSWER_SECTION_KEY, HTML_To_Fixed_String(Temp)));
+ when Wrong_Answer | Malformed_Answer =>
+ Insert(Translations_Answer, Assoc(ANSWER_KIND_KEY, ANSWER_KIND_BAD));
+ Insert(Translations_Answer, Assoc(ANSWER_MESSAGE_KEY, UB_Text_To_Fixed_String(Answer_Message)));
+ Insert(Translations_Answer, Assoc(Titration_Curve_Suite.TITRATION_CURVE_IMAGE_PATH_KEY, "/resources/noimage.png"));
+ Temp := Parse(Filename => "templates/titration_curve_answer_section.html", Translations => Translations_Answer);
+ Insert(Translations, Assoc(ANSWER_SECTION_KEY, HTML_To_Fixed_String(Temp)));
+ when others =>
+ Insert(Translations, Assoc(ANSWER_SECTION_KEY, ""));
+ end case;
+
+ Temp := Parse(Filename => "templates/face_titration_curve.html", Translations => Translations);
+
+ Append_HTML(Source => HTML, New_Item => Temp);
+
+ Temp := Parse(Filename => "templates/footer.html");
+ Append_HTML(Source => HTML, New_Item => Temp);
+
+ return OK;
+ end Generate_Face_Titration_Curve;
+
end Face_Generator;
+with Ada.Numerics.Discrete_Random;
+with Ada.Numerics.Float_Random;
+with Ada.Numerics.Generic_Elementary_Functions;
+with Ada.Directories;
+with Ada.Strings.Fixed;
+with Ada.Strings.Unbounded;
+with Formatting_Helpers;
+with Cairo.Png;
+with Cairo.Image_Surface;
+with Glib;
+with Interfaces.C;
+with Interfaces.C.Strings;
+
+with Ada.Text_IO;
+
separate(Problem_Generator)
package body Titration_Curve_Suite is
-- BEGIN: Inherited functions
function Create return access Titration_Curve_Problem is
+ Problem: access Titration_Curve_Problem;
begin
- return new Titration_Curve_Problem;
+ Problem := new Titration_Curve_Problem;
+ return Problem;
end Create;
function Check_Answer(Problem: in out Titration_Curve_Problem; Answer: in Answer_Info.Map;
Message: out UB_Text) return Answer_RetCode is
+ package FH is new Formatting_Helpers(T_Float);
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ package TFIO is new Ada.Text_IO.Float_IO(T_Float);
+ use FH;
+ use TFEF;
+ use TFIO;
+
+ Kx1: T_Float := 10.0 ** (-Problem.pKx1);
+ Kx2: T_Float := 10.0 ** (-Problem.pKx2);
+ pH_Start: T_Float;
+ pH_Half_First: T_Float;
+ pH_First_Equiv: T_Float;
+ pH_Half_Second: T_Float;
+ pH_Second_Equiv: T_Float;
+ pH_Over_Second_Equiv: T_Float;
+
+ V_Half_First: T_Float;
+ V_First_Equiv: T_Float;
+ V_Half_Second: T_Float;
+ V_Second_Equiv: T_Float;
+ V_Over_Second_Equiv: T_Float;
+
+ First_Guess: T_Float;
+
+ pH_Start_Ans: T_Float;
+ pH_First_Half_Ans: T_Float;
+ pH_First_Equiv_Ans: T_Float;
+ pH_Second_Half_Ans: T_Float;
+ pH_Second_Equiv_Ans: T_Float;
+ pH_Over_Second_Equiv_Ans: T_Float;
+
+ V_First_Half_Ans: T_Float;
+ V_First_Equiv_Ans: T_Float;
+ V_Second_Half_Ans: T_Float;
+ V_Second_Equiv_Ans: T_Float;
+ V_Over_Second_Equiv_Ans: T_Float;
+
+ Surface: Cairo.Cairo_Surface;
+ Ctx: Cairo.Cairo_Context;
+ Status_Out: Cairo.Cairo_Status;
begin
- return No_Answer;
+ -- Init Cairo
+ Surface := Cairo.Image_Surface.Create(Cairo.Image_Surface.CAIRO_FORMAT_ARGB32, IMAGE_WIDTH, IMAGE_HEIGHT);
+ Ctx := Cairo.Create(Surface);
+
+ pH_Start := -Log(Base => 10.0, X => (Kx1 * Problem.Sample_Concentration) ** 0.5);
+ pH_Half_First := -Log(Base => 10.0, X => Kx1);
+ pH_First_Equiv := 0.5 * (-Log(Base => 10.0, X => Kx1) - Log(Base => 10.0, X => Kx2));
+ pH_Half_Second := -Log(Base => 10.0, X => Kx2);
+
+ V_Half_First := (Problem.Sample_Volume * Problem.Sample_Concentration / Problem.T_Concentration) * 0.5;
+ V_First_Equiv := V_Half_First * 2.0;
+ V_Half_Second := V_Half_First + V_First_Equiv;
+ V_Second_Equiv := V_First_Equiv * 2.0;
+ V_Over_Second_Equiv := V_Second_Equiv * 1.5;
+
+ case Problem.SType is
+ when ACID =>
+ pH_Second_Equiv := -Log(Base => 10.0, X => (((Kx2) * KW) / (Problem.Sample_Concentration * Problem.Sample_Volume / V_Second_Equiv)) ** 0.5);
+ pH_Over_Second_Equiv := 14.0 + Log(Base => 10.0, X => (Problem.T_Concentration * V_Over_Second_Equiv - (2.0 * Problem.Sample_Concentration * Problem.Sample_Volume)) / (V_Over_Second_Equiv + Problem.Sample_Volume));
+ when BASE =>
+ pH_Start := 14.0 - pH_Start;
+ pH_Half_First := 14.0 - pH_Half_First;
+ pH_First_Equiv := 14.0 - pH_First_Equiv;
+ pH_Half_Second := 14.0 - pH_Half_Second;
+ pH_Second_Equiv := -Log(Base => 10.0, X => ((KW * (Problem.Sample_Concentration * Problem.Sample_Volume / V_Second_Equiv)) / Kx2) ** 0.5);
+ pH_Over_Second_Equiv := -Log(Base => 10.0, X => (Problem.T_Concentration * V_Over_Second_Equiv - (2.0 * Problem.Sample_Concentration * Problem.Sample_Volume)) / (V_Over_Second_Equiv + Problem.Sample_Volume));
+ end case;
+
+ --Ada.Text_IO.Put_Line("Sample type: " & Sample_Type'Image(Problem.SType));
+ --Ada.Text_IO.Put("Sample concentration: "); TFIO.Put(Problem.Sample_Concentration); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("Sample Volume: "); TFIO.Put(Problem.Sample_Volume); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("Titrimetric solution: "); TFIO.Put(Problem.T_Concentration); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("pKx1: "); TFIO.Put(-Log(Base => 10.0, X => Kx1)); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("pKx2: "); TFIO.Put(-Log(Base => 10.0, X => Kx2)); Ada.Text_IO.New_Line;
+
+ --Ada.Text_IO.Put("pH_Start: "); TFIO.Put(pH_Start); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("V_1_H: "); TFIO.Put(V_Half_First); Ada.Text_IO.Put(" pH_1_H: "); TFIO.Put(pH_Half_First); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("V_1_Eq: "); TFIO.Put(V_First_Equiv); Ada.Text_IO.Put(" V_1_Eq: "); TFIO.Put(pH_First_Equiv); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("V_2_H: "); TFIO.Put(V_Half_Second); Ada.Text_IO.Put(" pH_2_H: "); TFIO.Put(pH_Half_Second); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("V_2_Eq: "); TFIO.Put(V_Second_Equiv); Ada.Text_IO.Put(" pH_2_Eq: "); TFIO.Put(pH_Second_Equiv); Ada.Text_IO.New_Line;
+ --Ada.Text_IO.Put("V_2_1.5x: "); TFIO.Put(V_Over_Second_Equiv); Ada.Text_IO.Put(" pH_2_1.5x: "); TFIO.Put(pH_Over_Second_Equiv); Ada.Text_IO.New_Line;
+
+ case Problem.SType is
+ when ACID =>
+ First_Guess := 10.0 ** (-pH_Start);
+ when BASE =>
+ First_Guess := 10.0 ** (-pH_Over_Second_Equiv);
+ end case;
+
+ declare
+ pH_Start_Ans_S: constant String := Answer.Element(ANSWER_PH_START_KEY);
+ pH_First_Half_Ans_S: constant String := Answer.Element(ANSWER_PH_FIRST_HALF_KEY);
+ pH_First_Equiv_Ans_S: constant String := Answer.Element(ANSWER_PH_FIRST_EQUIV_KEY);
+ pH_Second_Half_Ans_S: constant String := Answer.Element(ANSWER_PH_SECOND_HALF_KEY);
+ pH_Second_Equiv_Ans_S: constant String := Answer.Element(ANSWER_PH_SECOND_EQUIV_KEY);
+ pH_Over_Second_Equiv_Ans_S: constant String := Answer.Element(ANSWER_PH_OVER_SECOND_EQUIV_KEY);
+ --
+ V_First_Half_Ans_S: constant String := Answer.Element(ANSWER_VOLUME_FIRST_HALF_KEY);
+ V_First_Equiv_Ans_S: constant String := Answer.Element(ANSWER_VOLUME_FIRST_EQUIV_KEY);
+ V_Second_Half_Ans_S: constant String := Answer.Element(ANSWER_VOLUME_SECOND_HALF_KEY);
+ V_Second_Equiv_Ans_S: constant String := Answer.Element(ANSWER_VOLUME_SECOND_EQUIV_KEY);
+ V_Over_Second_Equiv_Ans_S: constant String := Answer.Element(ANSWER_VOLUME_OVER_SECOND_EQUIV_KEY);
+ begin
+ pH_Start_Ans := FH.String_To_Float(pH_Start_Ans_S);
+ pH_First_Half_Ans := FH.String_To_Float(pH_First_Half_Ans_S);
+ pH_First_Equiv_Ans := FH.String_To_Float(pH_First_Equiv_Ans_S);
+ pH_Second_Half_Ans := FH.String_To_Float(pH_Second_Half_Ans_S);
+ pH_Second_Equiv_Ans := FH.String_To_Float(pH_Second_Equiv_Ans_S);
+ pH_Over_Second_Equiv_Ans := FH.String_To_Float(pH_Over_Second_Equiv_Ans_S);
+
+ V_First_Half_Ans := FH.String_To_Float(V_First_Half_Ans_S) / 1000.0;
+ V_First_Equiv_Ans := FH.String_To_Float(V_First_Equiv_Ans_S) / 1000.0;
+ V_Second_Half_Ans := FH.String_To_Float(V_Second_Half_Ans_S) / 1000.0;
+ V_Second_Equiv_Ans := FH.String_To_Float(V_Second_Equiv_Ans_S) / 1000.0;
+ V_Over_Second_Equiv_Ans := FH.String_To_Float(V_Over_Second_Equiv_Ans_S) / 1000.0;
+ exception
+ when Constraint_Error =>
+ Message := To_UB_Text("Nesprávně zadané údaje");
+ return Malformed_Answer;
+ end;
+
+ Prepare_Chart(Ctx, V_Over_Second_Equiv);
+ Draw_Titration_Curve(Ctx, Problem.SType, Kx1, Kx2, Problem.Sample_Concentration, Problem.T_Concentration, Problem.Sample_Volume, V_Over_Second_Equiv, First_Guess);
+ -- Draw interesting points
+ Draw_Chart_Crosshair(Ctx, 0.0, V_Over_Second_Equiv, pH_Start);
+ Draw_Chart_Crosshair(Ctx, V_Half_First, V_Over_Second_Equiv, pH_Half_First);
+ Draw_Chart_Crosshair(Ctx, V_First_Equiv, V_Over_Second_Equiv, pH_First_Equiv);
+ Draw_Chart_Crosshair(Ctx, V_Half_Second, V_Over_Second_Equiv, pH_Half_Second);
+ Draw_Chart_Crosshair(Ctx, V_Second_Equiv, V_Over_Second_Equiv, pH_Second_Equiv);
+ Draw_Chart_Crosshair(Ctx, V_Over_Second_Equiv, V_Over_Second_Equiv, pH_Over_Second_Equiv);
+
+ Draw_Chart_Circle(Ctx, 0.0, V_Over_Second_Equiv, pH_Start_Ans);
+ Draw_Chart_Circle(Ctx, V_First_Half_Ans, V_Over_Second_Equiv, pH_First_Half_Ans);
+ Draw_Chart_Circle(Ctx, V_First_Equiv_Ans, V_Over_Second_Equiv, pH_First_Equiv_Ans);
+ Draw_Chart_Circle(Ctx, V_Second_Half_Ans, V_Over_Second_Equiv, pH_Second_Half_Ans);
+ Draw_Chart_Circle(Ctx, V_Second_Equiv_Ans, V_Over_Second_Equiv, pH_Second_Equiv_Ans);
+ Draw_Chart_Circle(Ctx, V_Over_Second_Equiv_Ans, V_Over_Second_Equiv, pH_Over_Second_Equiv_Ans);
+
+ Status_Out := Cairo.Png.Write_To_Png(Surface, Ada.Strings.Unbounded.To_String(Problem.Resource_Prefix) & TITRATION_CURVE_FILENAME);
+ Message := To_UB_Text("");
+ return Correct_Answer;
end Check_Answer;
- function Get_Assignment(Problem: in out Titration_Curve_Problem; Assignment: in out Assignment_Info.Map) return RetCode is
+ function Get_Assignment(Problem: in out Titration_Curve_Problem; Assignment: in out Assignment_Info.Map; Resource_Prefix: in String) return RetCode is
+ package FH is new Formatting_Helpers(T_Float);
+ use FH;
+
+ Sample_Conc_Int, Sample_Conc_Dec, Sample_Conc_Exp: UB_Text;
+ Sample_Volume_Int, Sample_Volume_Dec, Sample_Volume_Exp: UB_Text;
+ Titrant_Conc_Int, Titrant_Conc_Dec, Titrant_Conc_Exp: UB_Text;
+ pKx1_Int, pKx1_Dec: UB_Text;
+ pKx2_Int, pKx2_Dec: UB_Text;
begin
- return E_INVAL;
+ Problem.Resource_Prefix := Ada.Strings.Unbounded.To_Unbounded_String(Resource_Prefix);
+
+ Split_Integer_Decimal_Exponent_Strs(Problem.Sample_Concentration, DECIMALS, Sample_Conc_Int, Sample_Conc_Dec, Sample_Conc_Exp);
+ Split_Integer_Decimal_Exponent_Strs(Problem.Sample_Volume, DECIMALS, Sample_Volume_Int, Sample_Volume_Dec, Sample_Volume_Exp);
+ Split_Integer_Decimal_Exponent_Strs(Problem.T_Concentration, DECIMALS, Titrant_Conc_Int, Titrant_Conc_Dec, Titrant_Conc_Exp);
+ Split_Integer_Decimal_Unscaled_Strs(Problem.pKx1, DECIMALS, pKx1_Int, pKx1_Dec);
+ Split_Integer_Decimal_Unscaled_Strs(Problem.pKx2, DECIMALS, pKx2_Int, pKx2_Dec);
+
+ Assignment.Insert(PROBLEM_TYPE_KEY, PROBLEM_TYPE_TITRATION_CURVE);
+ case Problem.SType is
+ when ACID =>
+ Assignment.Insert(SAMPLE_TYPE_KEY, SAMPLE_TYPE_ACID);
+ Assignment.Insert(TITRANT_TYPE_KEY, TITRANT_TYPE_BASE);
+ when BASE =>
+ Assignment.Insert(SAMPLE_TYPE_KEY, SAMPLE_TYPE_BASE);
+ Assignment.Insert(TITRANT_TYPE_KEY, TITRANT_TYPE_ACID);
+ end case;
+ Assignment.Insert(SAMPLE_CONC_INT_KEY, UB_Text_To_Fixed_String(Sample_Conc_Int));
+ Assignment.Insert(SAMPLE_CONC_DEC_KEY, UB_Text_To_Fixed_String(Sample_Conc_Dec));
+ Assignment.Insert(SAMPLE_CONC_EXP_KEY, UB_Text_To_Fixed_String(Sample_Conc_Exp));
+ --
+ Assignment.Insert(SAMPLE_VOLUME_INT_KEY, UB_Text_To_Fixed_String(Sample_Volume_Int));
+ Assignment.Insert(SAMPLE_VOLUME_DEC_KEY, UB_Text_To_Fixed_String(Sample_Volume_Dec));
+ Assignment.Insert(SAMPLE_VOLUME_EXP_KEY, UB_Text_To_Fixed_String(Sample_Volume_Exp));
+ --
+ Assignment.Insert(TITRANT_CONC_INT_KEY, UB_Text_To_Fixed_String(Titrant_Conc_Int));
+ Assignment.Insert(TITRANT_CONC_DEC_KEY, UB_Text_To_Fixed_String(Titrant_Conc_Dec));
+ Assignment.Insert(TITRANT_CONC_EXP_KEY, UB_Text_To_Fixed_String(Titrant_Conc_Exp));
+ --
+ Assignment.Insert(PKX1_INT_KEY, UB_Text_To_Fixed_String(pKx1_Int));
+ Assignment.Insert(PKX1_DEC_KEY, UB_Text_To_Fixed_String(pKx1_Dec));
+ Assignment.Insert(PKX2_INT_KEY, UB_Text_To_Fixed_String(pKx2_Int));
+ Assignment.Insert(PKX2_DEC_KEY, UB_Text_To_Fixed_String(pKx2_Dec));
+ -- NOTE: The image will be created only when user requests to check their answer
+ Assignment.Insert(TITRATION_CURVE_IMAGE_PATH_KEY, Ada.Strings.Unbounded.To_String(Problem.Resource_Prefix) & TITRATION_CURVE_FILENAME);
+
+ return OK;
end Get_Assignment;
function Get_Parameters(Problem: in out Titration_Curve_Problem; Parameters: out Parameters_Info.Map) return RetCode is
begin
- return E_INVAL;
+ return OK;
end Get_Parameters;
procedure New_Problem(Problem: in out Titration_Curve_Problem) is
+ package FH is new Formatting_Helpers(T_Float);
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ package Random_Sample_Type_Gen is new Ada.Numerics.Discrete_Random(Result_Subtype => Sample_Type);
+ use FH;
+ use TFEF;
+
+ PKX_1_RANGE: constant T_Float := MAX_PKX_1 - MIN_PKX_1;
+ SAMPLE_CONCENTRATION_RANGE: constant T_Float := Log(Base => 10.0, X => MAX_SAMPLE_CONCENTRATION) - Log(Base => 10.0, X => MIN_SAMPLE_CONCENTRATION);
+ Float_Seed: Ada.Numerics.Float_Random.Generator;
+ SType_Seed: Random_Sample_Type_Gen.Generator;
begin
- null;
+ -- Generate sample type
+ Random_Sample_Type_Gen.Reset(Gen => SType_Seed);
+ Problem.SType := Random_Sample_Type_Gen.Random(Gen => SType_Seed);
+
+ -- Generate pKx1
+ Ada.Numerics.Float_Random.Reset(Gen => Float_Seed);
+ Problem.pKx1 := Round_To_Valid_Nums((T_Float(Ada.Numerics.Float_Random.Random(Gen => Float_Seed)) * PKX_1_RANGE + MIN_PKX_1), DECIMALS);
+
+ -- Generate pKx2
+ declare
+ MIN_PKX_2: constant T_Float := Problem.pKx1 + PKX_STEP;
+ PKX_2_RANGE: constant T_Float := MAX_PKX_2 - MIN_PKX_2;
+ begin
+ Problem.pKx2 := Round_To_Valid_Nums((T_Float(Ada.Numerics.Float_Random.Random(Gen => Float_Seed)) * PKX_2_RANGE + MIN_PKX_2), DECIMALS);
+ end;
+
+ -- Generate sample concentration
+ Problem.Sample_Concentration := Round_To_Valid_Nums((10.0 ** (T_Float(Ada.Numerics.Float_Random.Random(Gen => Float_Seed)) * SAMPLE_CONCENTRATION_RANGE + Log(Base => 10.0, X => MIN_SAMPLE_CONCENTRATION))), DECIMALS);
+ -- Generate concentration of titrimetric solution
+ declare
+ TS_MIN: constant T_Float := Problem.Sample_Concentration / 10.0;
+ TS_MAX: constant T_Float := Problem.Sample_Concentration * 10.0;
+ TS_RANGE: constant T_Float := TS_MAX - TS_MIN;
+ begin
+ Problem.T_Concentration := Round_To_Valid_Nums((T_Float(Ada.Numerics.Float_Random.Random(Gen => Float_Seed)) * TS_RANGE + TS_MIN), DECIMALS);
+ end;
+
+ -- Generate sample volume
+ declare
+ VOLUME_RANGE: constant T_Float := (MAX_VOLUME_ML - MIN_VOLUME_ML) / 1000.0;
+ begin
+ Problem.Sample_Volume := Round_To_Valid_Nums(T_Float(Ada.Numerics.Float_Random.Random(Gen => Float_Seed)) * VOLUME_RANGE + (MIN_VOLUME_ML / 1000.0), DECIMALS);
+ end;
end New_problem;
function Set_Parameters(Problem: in out Titration_Curve_Problem; Parameters: in Parameters_Info.Map) return RetCode is
begin
- return E_INVAL;
+ return OK;
end Set_Parameters;
+ overriding procedure Finalize(Problem: in out Titration_Curve_Problem) is
+ Path: constant String := Ada.Strings.Unbounded.To_String(Problem.Resource_Prefix) & TITRATION_CURVE_FILENAME;
+ File: Ada.Text_IO.File_Type;
+ begin
+ begin
+ Ada.Text_IO.Open(File => File, Mode => Ada.Text_IO.In_File, Name => Path);
+ Ada.Text_IO.Close(File);
+ Ada.Directories.Delete_File(Path);
+ exception
+ when Ada.Text_IO.Name_Error =>
+ null;
+ end;
+ end Finalize;
+ -- END: Inherited functions
+
+ -- BEGIN: Private functions
+
+ function Calculate_1st_Diff_Acid(cA: in T_Float; Ka1: in T_Float; Ka2: in T_Float; cB: in T_Float; Kb: in T_Float; Xn: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ A: constant T_Float := -Kb;
+ B: constant T_Float := -(Kb * cB) - Kw - (Ka1 * Kb);
+ C: constant T_Float := (cA * Ka1 * Kb) + (Kb * KW) - (Ka1 * Kb * cB) - (Ka1 * KW) - (Ka1 * Ka2 * Kb);
+ D: constant T_Float := (cA * Ka1 * KW) + (2.0 * cA * Ka1 * Ka2 * Kb) + (KW * KW) + (Ka1 * Kb * KW) - (Ka1 * Ka2 * Kb * cB) - (Ka1 * Ka2 * KW);
+ E: constant T_Float := (2.0 * cA * Ka1 * Ka2 * KW) + (Ka1 * KW * KW) + (Ka1 * Ka2 * Kb * KW);
+ begin
+ return (5.0 * A * (Xn ** 4)) + (4.0 * B * (Xn ** 3)) + (3.0 * C * (Xn ** 2)) + (2.0 * D * Xn) + E;
+ end Calculate_1st_Diff_Acid;
+
+ function Calculate_2nd_Diff_Acid(cA: in T_Float; Ka1: in T_Float; Ka2: in T_Float; cB: in T_Float; Kb: in T_Float; Xn: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ A: constant T_Float := -Kb;
+ B: constant T_Float := -(Kb * cB) - Kw - (Ka1 * Kb);
+ C: constant T_Float := (cA * Ka1 * Kb) + (Kb * KW) - (Ka1 * Kb * cB) - (Ka1 * KW) - (Ka1 * Ka2 * Kb);
+ D: constant T_Float := (cA * Ka1 * KW) + (2.0 * cA * Ka1 * Ka2 * Kb) + (KW * KW) + (Ka1 * Kb * KW) - (Ka1 * Ka2 * Kb * cB) - (Ka1 * Ka2 * KW);
+ begin
+ return (20.0 * A * (Xn ** 3)) + (12.0 * B * (Xn ** 2)) + (6.0 * C * Xn) + (2.0 * D);
+ end Calculate_2nd_Diff_Acid;
+
+ function Calculate_1st_Diff_Base(cB: in T_Float; Kb1: in T_Float; Kb2: in T_Float; cA: in T_Float; Ka: in T_Float; Xn: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ A: constant T_Float := -(Kb1 * Kb2);
+ B: constant T_Float := -(2.0 * cB * Kb1 * Kb2) - (Kb1 * KW) - (Ka * Kb1 * Kb2);
+ C: constant T_Float := (cA * Ka * Kb1 * Kb2) + (Kb1 * Kb2 * KW) - (cB * KB1 * KW) - (2.0 * cB * Ka * Kb1 * Kb2) - (KW ** 2) - (Ka * Kb1 * KW);
+ D: constant T_Float := (cA * Ka * Kb1 * KW) + (Kb1 * (KW ** 2)) + (Ka * Kb1 * Kb2 * KW) - (cB * Ka * Kb1 * KW) - (Ka * (KW ** 2));
+ E: constant T_Float := (cA * Ka * (KW ** 2)) + (Ka * Kb1 * (KW ** 2)) + (KW ** 3);
+ begin
+ return (5.0 * A * (Xn ** 4)) + (4.0 * B * (Xn ** 3)) + (3.0 * C * (Xn ** 2)) + (2.0 * D * Xn) + E;
+ end Calculate_1st_Diff_Base;
+
+ function Calculate_2nd_Diff_Base(cB: in T_Float; Kb1: in T_Float; Kb2: in T_Float; cA: in T_Float; Ka: in T_Float; Xn: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ A: constant T_Float := -(Kb1 * Kb2);
+ B: constant T_Float := -(2.0 * cB * Kb1 * Kb2) - (Kb1 * KW) - (Ka * Kb1 * Kb2);
+ C: constant T_Float := (cA * Ka * Kb1 * Kb2) + (Kb1 * Kb2 * KW) - (cB * KB1 * KW) - (2.0 * cB * Ka * Kb1 * Kb2) - (KW ** 2) - (Ka * Kb1 * KW);
+ D: constant T_Float := (cA * Ka * Kb1 * KW) + (Kb1 * (KW ** 2)) + (Ka * Kb1 * Kb2 * KW) - (cB * Ka * Kb1 * KW) - (Ka * (KW ** 2));
+ begin
+ return (20.0 * A * (Xn ** 3)) + (12.0 * B * (Xn ** 2)) + (6.0 * C * Xn) + (2.0 * D);
+ end Calculate_2nd_Diff_Base;
+
+ function Calculate_Full_Acid(cA: in T_Float; Ka1: in T_Float; Ka2: in T_Float; cB: in T_Float; Kb: in T_Float; Xn: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ A: constant T_Float := -Kb;
+ B: constant T_Float := -(Kb * cB) - KW - (Ka1 * Kb);
+ C: constant T_Float := (cA * Ka1 * Kb) + (Kb * KW) - (Ka1 * Kb * cB) - (Ka1 * KW) - (Ka1 * Ka2 * Kb);
+ D: constant T_Float := (cA * Ka1 * KW) + (2.0 * cA * Ka1 * Ka2 * Kb) + (KW * KW) + (Ka1 * Kb * KW) - (Ka1 * Ka2 * Kb * cB) - (Ka1 * Ka2 * KW);
+ E: constant T_Float := (2.0 * cA * Ka1 * Ka2 * KW) + (Ka1 * KW * KW) + (Ka1 * Ka2 * Kb * KW);
+ F: constant T_Float := Ka1 * Ka2 * KW * KW;
+ begin
+ return (A * (Xn ** 5)) + (B * (Xn ** 4)) + (C * (Xn ** 3)) + (D * (Xn ** 2)) + (E * Xn) + F;
+ end Calculate_Full_Acid;
+
+ function Calculate_Full_Base(cB: in T_Float; Kb1: in T_Float; Kb2: in T_Float; cA: in T_Float; Ka: in T_Float; Xn: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ A: constant T_Float := -(Kb1 * Kb2);
+ B: constant T_Float := -(2.0 * cB * Kb1 * Kb2) - (Kb1 * KW) - (Ka * Kb1 * Kb2);
+ C: constant T_Float := (cA * Ka * Kb1 * Kb2) + (KW ** 3) + (Kb1 * Kb2 * KW) - (cB * Kb1 * KW) - (2.0 * cB * Ka * Kb1 * Kb2) - (KW ** 2) - (Ka * Kb1 * KW);
+ D: constant T_Float := (cA * Ka * Kb1 * KW) + (Kb1 * (KW ** 2)) + (Ka * Kb1 * Kb2 * KW) - (cB * Ka * Kb1 * KW) - (Ka * (KW ** 2));
+ E: constant T_Float := (cA * Ka * (KW ** 2)) + (Ka * Kb1 * KW * KW) + (KW * KW * KW);
+ F: constant T_Float := Ka * KW * KW * KW;
+ begin
+ return (A * (Xn ** 5)) + (B * (Xn ** 4)) + (C * (Xn ** 3)) + (D * (Xn ** 2)) + (E * Xn) + F;
+ end Calculate_Full_Base;
+
+ function Calculate_Target_End_Value(Num: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ E: T_Float;
+ Scaled: T_Float;
+ begin
+ E := Log(Base => 10.0, X => Num);
+ E := T_Float'Floor(E);
+ E := 10.0 ** E;
+ Scaled := Num / E;
+ Scaled := T_Float'Ceiling(Scaled);
+
+ return Scaled * E;
+ end Calculate_Target_End_Value;
+
+ procedure Draw_Chart_Circle(Ctx: in out Cairo.Cairo_Context; X_Raw: in T_Float; X_Range_Raw: in T_Float; Y_Raw: in T_Float; Y_Range_Raw: in T_Float := 14.0) is
+ X: constant Glib.GDouble := Glib.GDouble(X_Raw * T_Float(IMAGE_CHART_WIDTH) / X_Range_Raw) + X_OFFSET;
+ Y: constant Glib.GDouble := Glib.GDouble(T_Float(IMAGE_CHART_HEIGHT) - (Y_Raw * T_Float(IMAGE_CHART_HEIGHT) / Y_Range_Raw)) + Y_OFFSET;
+ begin
+ Cairo.Set_Source_Rgb(Ctx, 0.0, 0.0, 0.7);
+ Cairo.Set_Line_Width(Ctx, CROSSHAIR_THICKNESS);
+ Cairo.Move_To(Ctx, X, Y);
+ Cairo.Arc(Ctx, X, Y, 5.0, 0.0, Glib.GDouble(Ada.Numerics.PI * 2.0));
+ Cairo.Stroke(Ctx);
+ end Draw_Chart_Circle;
+
+ procedure Draw_Chart_Crosshair(Ctx: in out Cairo.Cairo_Context; X_Raw: in T_Float; X_Range_Raw: in T_Float; Y_Raw: in T_Float; Y_Range_Raw: in T_Float := 14.0) is
+ XV: constant Glib.GDouble := Glib.GDouble(X_Raw * T_Float(IMAGE_CHART_WIDTH) / X_Range_Raw) + X_OFFSET;
+ YV: constant Glib.GDouble := Glib.GDouble(T_Float(IMAGE_CHART_HEIGHT) - (Y_Raw * T_Float(IMAGE_CHART_HEIGHT) / Y_Range_Raw)) + Y_OFFSET - (CROSSHAIR_LENGTH / 2.0);
+ XH: constant Glib.GDouble := Glib.GDouble(X_Raw * T_Float(IMAGE_CHART_WIDTH) / X_Range_Raw) + X_OFFSET - (CROSSHAIR_LENGTH / 2.0);
+ YH: constant Glib.GDouble := Glib.GDouble(T_Float(IMAGE_CHART_HEIGHT) - (Y_Raw * T_Float(IMAGE_CHART_HEIGHT) / Y_Range_Raw)) + Y_OFFSET;
+ begin
+ Cairo.Set_Source_Rgb(Ctx, 0.0, 0.7, 0.0);
+ Cairo.Set_Line_Width(Ctx, CROSSHAIR_THICKNESS);
+ Cairo.Move_To(Ctx, XV, YV);
+ Cairo.Line_To(Ctx, XV, YV + CROSSHAIR_LENGTH);
+ Cairo.Move_To(Ctx, XH, YH);
+ Cairo.Line_To(Ctx, XH + CROSSHAIR_LENGTH, YH);
+ Cairo.Stroke(Ctx);
+ end Draw_Chart_Crosshair;
+
+ procedure Draw_Titration_Curve(Ctx: in out Cairo.Cairo_Context; SType: in Sample_Type; Kx1: in T_Float; Kx2: in T_Float; c0_Sample: in T_Float; c0_TS: in T_Float; V_Sample: in T_Float; V_Final: in T_Float; First_Guess: in T_Float) is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ V_Step: constant T_Float := V_Final / 800.0;
+ V_TS_Now: T_Float;
+ V_Total_Now: T_Float;
+ Nothing_Drawn: Boolean := True;
+ c_H3O: T_Float := First_Guess;
+ Kz: T_Float;
+ begin
+ Cairo.Set_Source_Rgb(Ctx, 1.0, 0.0, 0.0);
+ Cairo.Set_Line_Width(Ctx, TC_LINE_THICKNESS);
+
+ case SType is
+ when ACID =>
+ Kz := TITR_BASE_KB;
+ -- Calculate acid titration curve from the starting point
+ V_TS_Now := 0.0;
+ V_Total_Now := V_Sample;
+ when BASE =>
+ Kz := TITR_ACID_KA;
+ -- Calculate base titration curve from the ending point
+ V_TS_Now := V_Final;
+ V_Total_Now := V_Sample + V_Final;
+ end case;
+
+ loop
+ declare
+ c_TS: constant T_Float := c0_TS * V_TS_Now / V_Total_Now;
+ c_Sample: constant T_Float := c0_Sample * V_Sample / V_Total_Now;
+ pH: T_Float;
+ begin
+ c_H3O := Solve_Halleys_Approximation(SType, c_Sample, Kx1, Kx2, c_TS, Kz, c_H3O);
+ pH := -Log(Base => 10.0, X => c_H3O);
+ if Nothing_Drawn then
+ Cairo.Move_To(Ctx, (Glib.GDouble(V_TS_Now) * IMAGE_CHART_WIDTH) / Glib.GDouble(V_Final) + X_OFFSET, (Glib.GDouble(T_Float(IMAGE_CHART_HEIGHT) - (pH * T_Float(IMAGE_CHART_HEIGHT) / 14.0))) + Y_OFFSET);
+ Nothing_Drawn := False;
+ else
+ Draw_Titration_Curve_Point(Ctx, V_TS_Now, pH, V_Final);
+ end if;
+
+ --Ada.Text_IO.Put_Line("pH = " & T_Float'Image(pH) & " , V = " & T_Float'Image(V_TS_Now) & " c_Sample = " & T_Float'Image(c_Sample) & " , c_TS = " & T_Float'Image(c_TS));
+
+ case SType is
+ when ACID =>
+ V_TS_Now := V_TS_Now + V_Step;
+ V_Total_Now := V_Total_Now + V_Step;
+ exit when V_TS_Now >= V_Final;
+ when BASE =>
+ V_TS_Now := V_TS_Now - V_Step;
+ V_Total_Now := V_Total_Now - V_Step;
+ exit when V_TS_Now <= 0.0;
+ end case;
+ end;
+ end loop;
+ Cairo.Stroke(Ctx);
+
+ end Draw_Titration_Curve;
+
+ procedure Draw_Titration_Curve_Point(Ctx: in out Cairo.Cairo_Context; X_Raw: in T_Float; Y_Raw: in T_Float; X_Range_Raw: in T_Float; Y_Range_Raw: in T_Float := 14.0) is
+ X: constant Glib.GDouble := Glib.GDouble(X_Raw * T_Float(IMAGE_CHART_WIDTH) / X_Range_Raw) + X_OFFSET;
+ Y: constant Glib.GDouble := Glib.GDouble(T_Float(IMAGE_CHART_HEIGHT) - (Y_Raw * T_Float(IMAGE_CHART_HEIGHT) / Y_Range_Raw)) + Y_OFFSET;
+ begin
+ Cairo.Line_To(Ctx, X, Y);
+ Cairo.Move_To(Ctx, X, Y);
+ end Draw_Titration_Curve_Point;
+
+ procedure Prepare_Chart(Ctx: in out Cairo.Cairo_Context; V_Final: in T_Float) is
+ package TFIO is new Ada.Text_IO.Float_IO(T_Float);
+ use Interfaces.C.Strings;
+
+ Y_Step: constant Glib.GDouble := IMAGE_CHART_HEIGHT / 14.0;
+ begin
+ Cairo.Set_Font_Size(Ctx, FONT_SIZE);
+ -- Draw background
+ Cairo.Set_Source_Rgb(Ctx, 1.0, 1.0, 1.0);
+ Cairo.Rectangle(Ctx, 0.0, 0.0, Glib.GDouble(IMAGE_WIDTH), Glib.GDouble(IMAGE_HEIGHT));
+ Cairo.Stroke_Preserve(Ctx);
+ Cairo.Fill(Ctx);
+
+ -- Draw legend
+ Cairo.Set_Source_Rgb(Ctx, 0.0, 0.0, 0.7);
+ Cairo.Set_Line_Width(Ctx, CROSSHAIR_THICKNESS);
+ Cairo.Move_To(Ctx, LEGEND_X_USER_COORD, LEGEND_Y_COORD);
+ Cairo.Arc(Ctx, LEGEND_X_USER_COORD, LEGEND_Y_COORD, 5.0, 0.0, Glib.GDouble(Ada.Numerics.PI * 2.0));
+ Cairo.Stroke(Ctx);
+ Cairo.Set_Source_Rgb(Ctx, 0.0, 0.0, 0.0);
+ Cairo.Move_To(Ctx, LEGEND_X_USER_COORD + LEGEND_MARK_TEXT_X_OFFSET, LEGEND_Y_TEXT_COORD);
+ Cairo.Show_Text(Ctx, "Vaše odpověď");
+ Cairo.Stroke(Ctx);
+ --
+ Cairo.Set_Source_Rgb(Ctx, 0.0, 0.7, 0.0);
+ Cairo.Move_To(Ctx, LEGEND_X_CALCD_COORD - (CROSSHAIR_LENGTH / 2.0), LEGEND_Y_COORD);
+ Cairo.Line_To(Ctx, LEGEND_X_CALCD_COORD + (CROSSHAIR_LENGTH / 2.0), LEGEND_Y_COORD);
+ Cairo.Move_To(Ctx, LEGEND_X_CALCD_COORD, LEGEND_Y_COORD - (CROSSHAIR_LENGTH / 2.0));
+ Cairo.Line_To(Ctx, LEGEND_X_CALCD_COORD, LEGEND_Y_COORD + (CROSSHAIR_LENGTH / 2.0));
+ Cairo.Stroke(Ctx);
+ Cairo.Set_Source_Rgb(Ctx, 0.0, 0.0, 0.0);
+ Cairo.Move_To(Ctx, LEGEND_X_CALCD_COORD + LEGEND_MARK_TEXT_X_OFFSET, LEGEND_Y_TEXT_COORD);
+ Cairo.Show_Text(Ctx, "Vypočtená odpoveď");
+ Cairo.Stroke(Ctx);
+
+ -- Set line properties
+ Cairo.Set_Source_Rgb(Ctx, 0.0, 0.0, 0.0);
+ Cairo.Set_Line_Width(Ctx, AXIS_LINE_THICKNESS);
+ Cairo.Set_Font_Size(Ctx, FONT_SIZE);
+ -- Draw X-axis line
+ Cairo.Move_To(Ctx, X_OFFSET, IMAGE_CHART_HEIGHT + Y_OFFSET);
+ Cairo.Line_To(Ctx, Glib.GDouble(IMAGE_WIDTH) - IMAGE_RIGHT_BORDER_WIDTH, IMAGE_CHART_HEIGHT + Y_OFFSET);
+ -- Draw Y-axis line
+ Cairo.Move_To(Ctx, X_OFFSET, Y_OFFSET - (AXIS_LINE_THICKNESS / 2.0));
+ Cairo.Line_To(Ctx, X_OFFSET, IMAGE_CHART_HEIGHT + Y_OFFSET);
+
+ -- X-axis ticks and labels
+ declare
+ Real_End_Value: constant T_Float := V_Final * 1000.0;
+ Target_End_Value: constant T_Float := Calculate_Target_End_Value(V_Final * 1000.0);
+ Tick_Step: Glib.GDouble := Glib.GDouble(Target_End_Value);
+ R_T_Ratio: constant Glib.GDouble := Glib.GDouble(Target_End_Value / Real_End_value);
+ Divide_By: Glib.GDouble := 2.0;
+ Tick_Step_Pixels: Glib.GDouble;
+ begin
+ --Ada.Text_IO.Put("TEV [mL]: "); TFIO.Put(Target_End_Value); Ada.Text_IO.New_Line;
+
+ Tick_Step_Pixels := IMAGE_CHART_WIDTH / Tick_Step * R_T_Ratio;
+ while Tick_Step_Pixels < BIG_TICK_X_MIN_STEP loop
+ Tick_Step := Tick_Step / Divide_By;
+ if Divide_By = 2.0 then
+ Divide_By := 5.0;
+ else
+ Divide_By := 2.0;
+ end if;
+ Tick_Step_Pixels := IMAGE_CHART_WIDTH / Tick_Step * R_T_Ratio;
+ end loop;
+
+ --Ada.Text_IO.Put("X tick step [mL]: "); TFIO.Put(Target_End_Value / T_Float(Tick_Step)); Ada.Text_IO.Put(" | pixels: "); TFIO.Put(T_Float(Tick_Step_Pixels)); Ada.Text_IO.New_Line;
+
+ -- Draw ticks and labels
+ declare
+ Tick_Label_Step: constant Integer := Integer(Target_End_Value / T_Float(Tick_Step));
+ Tick_X_Pos: Glib.GDouble := X_OFFSET;
+ Tick_Label: Integer := 0;
+ Text_Extents: aliased Cairo.Cairo_Text_Extents;
+ begin
+ while Tick_X_Pos < IMAGE_CHART_WIDTH + X_OFFSET loop
+ declare
+ Tick_Label_CStrPtr: Interfaces.C.Strings.chars_ptr := Interfaces.C.Strings.New_Char_Array(Interfaces.C.To_C(Integer'Image(Tick_Label)));
+ begin
+ if Tick_Label_CStrPtr = Interfaces.C.Strings.Null_Ptr then
+ goto Skip_X_Tick;
+ end if;
+
+ -- Draw tick
+ Cairo.Move_To(Ctx, Tick_X_Pos, IMAGE_CHART_HEIGHT + Y_OFFSET);
+ Cairo.Line_To(Ctx, Tick_X_Pos, IMAGE_CHART_HEIGHT + BIG_TICK_LENGTH + Y_OFFSET);
+
+ -- Draw label
+ Cairo.Text_Extents(Ctx, Tick_Label_CStrPtr, Text_Extents'Access);
+ Cairo.Move_To(Ctx, Tick_X_Pos - (Text_Extents.X_Advance / 2.0), IMAGE_CHART_HEIGHT + BIG_TICK_LENGTH + Y_OFFSET + Text_Extents.Height + TEXT_SPACE);
+ Cairo.Show_Text(Ctx, Integer'Image(Tick_Label));
+ Interfaces.C.Strings.Free(Tick_Label_CStrPtr);
+
+ <<Skip_X_Tick>>
+ Tick_X_Pos := Tick_X_Pos + Tick_Step_Pixels;
+ Tick_Label := Tick_Label + Tick_Label_Step;
+ end;
+ end loop;
+ end;
+ end;
+
+ -- Y-axis ticks and labels
+ for Idx in 0 .. 14 loop
+ declare
+ Tick_Y_Pos: constant Glib.GDouble := Glib.GDouble(Idx) * Y_Step + Y_OFFSET;
+ Tick_Label_CStrPtr: Interfaces.C.Strings.chars_ptr;
+ Text_Extents: aliased Cairo.Cairo_Text_Extents;
+ begin
+ -- Draw tick
+ Cairo.Move_To(Ctx, X_OFFSET - BIG_TICK_LENGTH, Tick_Y_Pos);
+ Cairo.Line_To(Ctx, X_OFFSET, Tick_Y_Pos);
+
+ -- Draw label
+ Tick_Label_CStrPtr := Interfaces.C.Strings.New_Char_Array(Interfaces.C.To_C(Integer'Image(14 - Idx)));
+ if Tick_Label_CStrPtr /= Interfaces.C.Strings.Null_Ptr then
+ Cairo.Text_Extents(Ctx, Tick_Label_CStrPtr, Text_Extents'Access);
+ Cairo.Move_To(Ctx, X_OFFSET - Text_Extents.X_Advance - BIG_TICK_LENGTH - TEXT_SPACE, Tick_Y_Pos + (Text_Extents.Height / 2.0));
+ Cairo.Show_Text(Ctx, Integer'Image(14 - Idx));
+ Interfaces.C.Strings.Free(Tick_Label_CStrPtr);
+ end if;
+ end;
+ end loop;
+
+ -- Draw axis units
+ declare
+ Tick_Label_CStrPtr: Interfaces.C.Strings.chars_ptr;
+ Text_Extents: aliased Cairo.Cairo_Text_Extents;
+ begin
+ -- X-axis
+ Tick_Label_CStrPtr := Interfaces.C.Strings.New_Char_Array(Interfaces.C.To_C(X_AXIS_UNITS_TEXT));
+ if Tick_Label_CStrPtr /= Interfaces.C.Strings.Null_Ptr then
+ Cairo.Text_Extents(Ctx, Tick_Label_CStrPtr, Text_Extents'Access);
+ Cairo.Move_To(Ctx, X_OFFSET + (IMAGE_CHART_WIDTH / 2.0) - (Text_Extents.Width / 2.0), Glib.GDouble(IMAGE_HEIGHT) - TEXT_SPACE);
+ Cairo.Show_Text(Ctx, X_AXIS_UNITS_TEXT);
+ Interfaces.C.Strings.Free(Tick_Label_CStrPtr);
+ end if;
+
+ -- Y-axis
+ Tick_Label_CStrPtr := Interfaces.C.Strings.New_Char_Array(Interfaces.C.To_C(Y_AXIS_UNITS_TEXT));
+ if Tick_Label_CStrPtr /= Interfaces.C.Strings.Null_Ptr then
+ Cairo.Text_Extents(Ctx, Tick_Label_CStrPtr, Text_Extents'Access);
+ Cairo.Move_To(Ctx, TEXT_SPACE + Text_Extents.Height, Y_OFFSET + IMAGE_CHART_HEIGHT / (2.0) - (Text_Extents.X_Advance / 2.0));
+ Cairo.Save(Ctx);
+ Cairo.Rotate(Ctx, -(Ada.Numerics.PI / 2.0));
+ Cairo.Show_Text(Ctx, Y_AXIS_UNITS_TEXT);
+ Cairo.Restore(Ctx);
+ Interfaces.C.Strings.Free(Tick_Label_CStrPtr);
+ end if;
+ end;
+
+ Cairo.Stroke(Ctx);
+ end Prepare_Chart;
+
+ function Solve_Halleys_Approximation(SType: in Sample_Type; c_Sample: in T_Float; Kx1: in T_Float; Kx2: in T_Float; c_TS: in T_Float; Kz: in T_Float; First_Guess: in T_Float) return T_Float is
+ package TFEF is new Ada.Numerics.Generic_Elementary_Functions(T_Float);
+ use TFEF;
+
+ Xn, Xnp, I_Frac: T_Float;
+ Difference: T_Float;
+ Ctr: Positive := 1;
+ begin
+ Xn := First_Guess;
+
+ -- Ada.Text_IO.Put_Line("First guess: " & T_Float'Image(Xn));
+ loop
+ declare
+ vf: T_Float;
+ vfd: T_Float;
+ vfdd: T_Float;
+ begin
+ case SType is
+ when ACID =>
+ vf := Calculate_Full_Acid(c_Sample, Kx1, Kx2, c_TS, Kz, Xn);
+ vfd := Calculate_1st_Diff_Acid(c_Sample, Kx1, Kx2, c_TS, Kz, Xn);
+ vfdd := Calculate_2nd_Diff_Acid(c_Sample, Kx1, Kx2, c_TS, Kz, Xn);
+ when BASE =>
+ vf := Calculate_Full_Base(c_Sample, Kx1, Kx2, c_TS, Kz, Xn);
+ vfd := Calculate_1st_Diff_Base(c_Sample, Kx1, Kx2, c_TS, Kz, Xn);
+ vfdd := Calculate_2nd_Diff_Base(c_Sample, Kx1, Kx2, c_TS, Kz, Xn);
+ end case;
+ I_Frac := (2.0 * vf * vfd) / ((2.0 * (vfd ** 2)) - (vf * vfdd));
+ end;
+
+ Xnp := Xn - I_Frac;
+ --Ada.Text_IO.Put_Line("Ctr = " & Positive'Image(Ctr) & ", Xn = " & T_Float'Image(Xn) & " , Xnp = " & T_Float'Image(Xnp));
+ Difference := Abs(Log(Base => 10.0, X => Xnp) - Log(Base => 10.0, X => Xn));
+ exit when Difference < 0.001;
+ Ctr := Ctr + 1;
+ Xn := Xnp;
+ end loop;
+
+ --Ada.Text_IO.Put_Line("Result: " & T_Float'Image(Xnp) & ", iters: " & Positive'Image(Ctr));
+ return Xnp;
+ end Solve_Halleys_Approximation;
+
end Titration_Curve_Suite;
with Global_Types;
with Problem_Generator_Syswides;
with Ada.Finalization;
+with Ada.Strings.Unbounded;
+with Cairo;
+with Glib;
use Global_Types;
use Problem_Generator_Syswides;
package Problem_Generator is
- type Chem_Problem is abstract tagged limited private;
+ type Chem_Problem is abstract limited new Ada.Finalization.Limited_Controlled with private;
function Create return access Chem_Problem is abstract;
function Check_Answer(Problem: in out Chem_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode is abstract;
- function Get_Assignment(Problem: in out Chem_Problem; Assignment: in out Assignment_Info.Map) return RetCode is abstract;
+ function Get_Assignment(Problem: in out Chem_Problem; Assignment: in out Assignment_Info.Map; Resource_Prefix: in String) return RetCode is abstract;
function Get_Parameters(Problem: in out Chem_Problem; Parameters: out Parameters_Info.Map) return RetCode is abstract;
procedure New_Problem(Problem: in out Chem_Problem) is abstract;
function Set_Parameters(Problem: in out Chem_Problem; Parameters: in Parameters_Info.Map) return RetCode is abstract;
function Get_Problem(P_Type: in Problem_Type) return access Chem_Problem'Class;
private
- type Chem_Problem is abstract tagged limited
+ type Chem_Problem is abstract limited new Ada.Finalization.Limited_Controlled with
record
Mutex: aliased Simple_Mutex;
end record;
-- Inherited
function Check_Answer(Problem: in out Acidobazic_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode;
procedure New_Problem(Problem: in out Acidobazic_Problem);
- function Get_Assignment(Problem: in out Acidobazic_Problem; Assignment: in out Assignment_Info.Map) return RetCode;
+ function Get_Assignment(Problem: in out Acidobazic_Problem; Assignment: in out Assignment_Info.Map; Resource_Prefix: in String) return RetCode;
function Get_Parameters(Problem: in out Acidobazic_Problem; Parameters: out Parameters_Info.Map) return RetCode;
function Set_Parameters(Problem: in out Acidobazic_Problem; Parameters: in Parameters_Info.Map) return RetCode;
-- Inherited
function Check_Answer(Problem: in out Solubility_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode;
procedure New_Problem(Problem: in out Solubility_Problem);
- function Get_Assignment(Problem: in out Solubility_Problem; Assignment: in out Assignment_Info.Map) return RetCode;
+ function Get_Assignment(Problem: in out Solubility_Problem; Assignment: in out Assignment_Info.Map; Resource_Prefix: in String) return RetCode;
function Get_Parameters(Problem: in out Solubility_Problem; Parameters: out Parameters_Info.Map) return RetCode;
function Set_Parameters(Problem: in out Solubility_Problem; Parameters: in Parameters_Info.Map) return RetCode;
package Titration_Curve_Suite is
use Problem_Generator_Syswides.Titration_Curve_Suite;
+ use Glib;
type Titration_Curve_Problem is new Problem_Generator.Chem_Problem with private;
-- Constructor
function Create return access Titration_Curve_Problem;
-- Inherited
function Check_Answer(Problem: in out Titration_Curve_Problem; Answer: in Answer_Info.Map; Message: out UB_Text) return Answer_RetCode;
- function Get_Assignment(Problem: in out Titration_Curve_Problem; Assignment: in out Assignment_Info.Map) return RetCode;
+ function Get_Assignment(Problem: in out Titration_Curve_Problem; Assignment: in out Assignment_Info.Map; Resource_Prefix: in String) return RetCode;
function Get_Parameters(Problem: in out Titration_Curve_Problem; Parameters: out Parameters_Info.Map) return RetCode;
procedure New_Problem(Problem: in out Titration_Curve_Problem);
function Set_Parameters(Problem: in out Titration_Curve_Problem; Parameters: in Parameters_Info.Map) return RetCode;
+ overriding procedure Finalize(Problem: in out Titration_Curve_Problem);
private
+ Math_Limitation: exception;
type T_Float is digits 15;
- type Titration_Curve_Problem is new Problem_Generator.Chem_Problem with null record;
+ type Titration_Curve_Problem is new Problem_Generator.Chem_Problem with
+ record
+ SType: Sample_Type;
+ pKx1: T_Float;
+ pKx2: T_Float;
+ Resource_Prefix: Ada.Strings.Unbounded.Unbounded_String;
+ Sample_Volume: T_Float;
+ Sample_Concentration: T_Float;
+ T_Concentration: T_Float;
+ end record;
+
+ function Calculate_1st_Diff_Acid(cA: in T_Float; Ka1: in T_Float; Ka2: in T_Float; cB: in T_Float; Kb: in T_Float; Xn: in T_Float) return T_Float;
+ function Calculate_2nd_Diff_Acid(cA: in T_Float; Ka1: in T_Float; Ka2: in T_Float; cB: in T_Float; Kb: in T_Float; Xn: in T_Float) return T_Float;
+ function Calculate_1st_Diff_Base(cB: in T_Float; Kb1: in T_Float; Kb2: in T_Float; cA: in T_Float; Ka: in T_Float; Xn: in T_Float) return T_Float;
+ function Calculate_2nd_Diff_Base(cB: in T_Float; Kb1: in T_Float; Kb2: in T_Float; cA: in T_Float; Ka: in T_Float; Xn: in T_Float) return T_Float;
+ function Calculate_Full_Acid(cA: in T_Float; Ka1: in T_Float; Ka2: in T_Float; cB: in T_Float; Kb: in T_Float; Xn: in T_Float) return T_Float;
+ function Calculate_Full_Base(cB: in T_Float; Kb1: in T_Float; Kb2: in T_Float; cA: in T_Float; Ka: in T_Float; Xn: in T_Float) return T_Float;
+ function Calculate_Target_End_Value(Num: in T_Float) return T_Float;
+ procedure Draw_Chart_Circle(Ctx: in out Cairo.Cairo_Context; X_Raw: in T_Float; X_Range_Raw: in T_Float; Y_Raw: in T_Float; Y_Range_Raw: in T_Float := 14.0);
+ procedure Draw_Chart_Crosshair(Ctx: in out Cairo.Cairo_Context; X_Raw: in T_Float; X_Range_Raw: in T_Float; Y_Raw: in T_Float; Y_Range_Raw: in T_Float := 14.0);
+ procedure Draw_Titration_Curve(Ctx: in out Cairo.Cairo_Context; SType: in Sample_Type; Kx1: in T_Float; Kx2: in T_Float; c0_Sample: in T_Float; c0_TS: in T_Float; V_Sample: in T_Float; V_Final: in T_Float; First_Guess: in T_Float);
+ procedure Draw_Titration_Curve_Point(Ctx: in out Cairo.Cairo_Context; X_Raw: in T_Float; Y_Raw: in T_Float; X_Range_Raw: in T_Float; Y_Range_Raw: in T_Float := 14.0);
+ procedure Prepare_Chart(Ctx: in out Cairo.Cairo_Context; V_Final: in T_Float);
+ function Solve_Halleys_Approximation(SType: in Sample_Type; c_Sample: in T_Float; Kx1: in T_Float; Kx2: in T_Float; c_TS: in T_Float; Kz: in T_Float; First_Guess: in T_Float) return T_Float;
+
+ MIN_PKX_1: constant T_Float := 2.0;
+ MAX_PKX_1: constant T_Float := 4.2;
+ MAX_PKX_2: constant T_Float := 8.3;
+ PKX_STEP: constant T_Float := 4.0;
+ MIN_VOLUME_ML: constant T_Float := 5.0;
+ MAX_VOLUME_ML: constant T_Float := 60.0;
+ MIN_SAMPLE_CONCENTRATION: constant T_Float := 1.0E-3;
+ MAX_SAMPLE_CONCENTRATION: constant T_Float := 1.0;
+ --
+ TITR_ACID_KA: constant T_Float := 0.0001; -- Corresponds to HCl
+ --TITR_BASE_KB: constant T_Float := 0.631; -- Corresponds to NaOH
+ TITR_BASE_KB: constant T_Float := 0.0001; -- Corresponds to NaOH
+ KW: constant T_Float := 1.0E-14;
+ --
+ DECIMALS: constant T_Float := 1.0E3;
+ MAX_DIFFERENCE: constant := 3.0E-14;
+ --
+ IMAGE_WIDTH: constant Glib.GInt := 800;
+ IMAGE_HEIGHT: constant Glib.GInt := 800;
+ IMAGE_RIGHT_BORDER_WIDTH: constant Glib.GDouble := 35.0;
+ IMAGE_BOTTOM_BORDER_HEIGHT: constant Glib.GDouble := 45.0;
+ X_OFFSET: constant Glib.GDouble := 45.0;
+ Y_OFFSET: constant Glib.GDouble := 60.0;
+ IMAGE_CHART_WIDTH: constant Glib.GDouble := Glib.GDouble(IMAGE_WIDTH) - X_OFFSET - IMAGE_RIGHT_BORDER_WIDTH;
+ IMAGE_CHART_HEIGHT: constant Glib.GDouble := Glib.GDouble(IMAGE_HEIGHT) - Y_OFFSET - IMAGE_BOTTOM_BORDER_HEIGHT;
+ CROSSHAIR_LENGTH: constant Glib.GDouble := 12.0;
+ BIG_TICK_LENGTH: constant Glib.GDouble := 8.0;
+ BIG_TICK_X_MIN_STEP: constant Glib.GDouble := 40.0;
+ AXIS_LINE_THICKNESS: constant Glib.GDouble := 3.0;
+ CROSSHAIR_THICKNESS: constant Glib.GDouble := 1.0;
+ TC_LINE_THICKNESS: constant Glib.GDouble := 2.0;
+ FONT_SIZE: constant Glib.GDouble := 18.0;
+ TEXT_SPACE: constant Glib.GDouble := 5.0;
+ --
+ LEGEND_Y_COORD: constant Glib.GDouble := 30.0;
+ LEGEND_Y_TEXT_COORD: constant Glib.GDouble := 38.0;
+ LEGEND_X_USER_COORD: constant Glib.GDouble := 100.0;
+ LEGEND_X_CALCD_COORD: constant Glib.GDouble := 500.0;
+ LEGEND_MARK_TEXT_X_OFFSET: constant Glib.GDouble := 30.0;
+ --
+ X_AXIS_UNITS_TEXT: constant String := "V (odměrný roztok) [mL]";
+ Y_AXIS_UNITS_TEXT: constant String := "pH";
+ --
+ TITRATION_CURVE_FILENAME: constant String := "-curve.png";
end Titration_Curve_Suite;